Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP]: Add cargo-add (from cargo-edit) to cargo proper #5611

Closed
wants to merge 12 commits into from
Closed
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ tar = { version = "0.4.15", default-features = false }
tempfile = "3.0"
termcolor = "1.0"
toml = "0.4.2"
toml_edit = "0.1.1"
url = "1.1"
clap = "2.31.2"
unicode-width = "0.1.5"
Expand Down
59 changes: 59 additions & 0 deletions src/bin/cargo/commands/add.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use command_prelude::*;

use cargo::ops;

use super::install;

pub fn cli() -> App {
subcommand("add")
.about("Add a new dependency")
.arg(Arg::with_name("crate").empty_values(false).multiple(true))
.arg(
opt("version", "Specify a version to add from crates.io")
.alias("vers")
.value_name("VERSION"),
)
.arg(opt("git", "Git URL to add the specified crate from").value_name("URL"))
.arg(opt("branch", "Branch to use when add from git").value_name("BRANCH"))
.arg(opt("tag", "Tag to use when add from git").value_name("TAG"))
.arg(opt("rev", "Specific commit to use when adding from git").value_name("SHA"))
.arg(opt("path", "Filesystem path to local crate to add").value_name("PATH"))
.after_help(
"\
This command allows you to add a dependency to a Cargo.toml manifest file. If <crate> is a github
or gitlab repository URL, or a local path, `cargo add` will try to automatically get the crate name
and set the appropriate `--git` or `--path` value.

Please note that Cargo treats versions like \"1.2.3\" as \"^1.2.3\" (and that \"^1.2.3\" is specified
as \">=1.2.3 and <2.0.0\"). By default, `cargo add` will use this format, as it is the one that the
crates.io registry suggests. One goal of `cargo add` is to prevent you from using wildcard
dependencies (version set to \"*\").",
)
}

pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
let ws = args.workspace(config)?;
let compile_opts = args.compile_options(config, CompileMode::Build)?;

println!("cargo add subcommand executed");

let krates = args.values_of("crate")
.unwrap_or_default()
.collect::<Vec<_>>();

println!("crate {:?}", krates);

let (_from_cwd, source) = install::get_source_id(&config, &args, &krates)?;

let version = args.value_of("version");

ops::add(
&ws,
krates,
&source,
version,
&compile_opts,
)?;

Ok(())
}
42 changes: 23 additions & 19 deletions src/bin/cargo/commands/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,29 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
.unwrap_or_default()
.collect::<Vec<_>>();

let mut from_cwd = false;
let (from_cwd, source) = get_source_id(&config, &args, &krates)?;

let version = args.value_of("version");
let root = args.value_of("root");

if args.is_present("list") {
ops::install_list(root, config)?;
} else {
ops::install(
root,
krates,
&source,
from_cwd,
version,
&compile_opts,
args.is_present("force"),
)?;
}
Ok(())
}

pub fn get_source_id(config: &Config, args: &ArgMatches, krates: &Vec<&str>) -> Result<(bool, SourceId), CliError> {
let mut from_cwd = false;
let source = if let Some(url) = args.value_of("git") {
let url = url.to_url()?;
let gitref = if let Some(branch) = args.value_of("branch") {
Expand All @@ -108,22 +129,5 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
} else {
SourceId::crates_io(config)?
};

let version = args.value_of("version");
let root = args.value_of("root");

if args.is_present("list") {
ops::install_list(root, config)?;
} else {
ops::install(
root,
krates,
&source,
from_cwd,
version,
&compile_opts,
args.is_present("force"),
)?;
}
Ok(())
Ok((from_cwd, source))
}
3 changes: 3 additions & 0 deletions src/bin/cargo/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use command_prelude::*;

pub fn builtin() -> Vec<App> {
vec![
add::cli(),
bench::cli(),
build::cli(),
check::cli(),
Expand Down Expand Up @@ -37,6 +38,7 @@ pub fn builtin() -> Vec<App> {

pub fn builtin_exec(cmd: &str) -> Option<fn(&mut Config, &ArgMatches) -> CliResult> {
let f = match cmd {
"add" => add::exec,
"bench" => bench::exec,
"build" => build::exec,
"check" => check::exec,
Expand Down Expand Up @@ -72,6 +74,7 @@ pub fn builtin_exec(cmd: &str) -> Option<fn(&mut Config, &ArgMatches) -> CliResu
Some(f)
}

pub mod add;
pub mod bench;
pub mod build;
pub mod check;
Expand Down
1 change: 1 addition & 0 deletions src/cargo/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ extern crate tar;
extern crate tempfile;
extern crate termcolor;
extern crate toml;
extern crate toml_edit;
extern crate unicode_width;
extern crate url;

Expand Down
80 changes: 80 additions & 0 deletions src/cargo/ops/cargo_add.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use core::{SourceId, Workspace};

use ops::{self, cargo_install};
use sources::{SourceConfigMap};
use util::errors::{CargoResult};
use util::toml;

pub fn add(
ws: &Workspace,
krates: Vec<&str>,
source_id: &SourceId,
vers: Option<&str>,
opts: &ops::CompileOptions,
) -> CargoResult<()> {
let cwd = ws.config().cwd();
println!("cwd is {:?}", cwd);

let map = SourceConfigMap::new(opts.config)?;

let manifest_path = Some(toml::manifest::find(&Some(cwd.to_path_buf()))?);
let mut manifest = toml::manifest::Manifest::open(&manifest_path)?;

let mut needs_update = true;
for krate in krates {
add_one(
&map,
krate,
source_id,
vers,
opts,
needs_update,
&mut manifest,
)?;
needs_update = false;
}

let mut file = toml::manifest::Manifest::find_file(&manifest_path)?;
manifest.write_to_file(&mut file)?;

Ok(())
}

fn add_one(
map: &SourceConfigMap,
krate: &str,
source_id: &SourceId,
vers: Option<&str>,
opts: &ops::CompileOptions,
needs_update: bool,
manifest: &mut toml::manifest::Manifest,
) -> CargoResult<()> {
let (pkg, _source) = cargo_install::select_pkg(
map.load(source_id)?,
Some(krate),
vers,
opts.config,
needs_update,
&mut |_| {
bail!(
"must specify a crate to install from \
crates.io, or use --path or --git to \
specify alternate source"
)
},
)?;
println!("pkg {:?}", pkg);

let dependency = toml::dependency::Dependency::new(&krate)
.set_version(&format!("{}", pkg.version()));

println!("dependency is {:?}", dependency);

manifest.insert_into_table(&get_section(), &dependency)?;

Ok(())
}

fn get_section() -> Vec<String> {
vec!["dependencies".to_owned()]
}
2 changes: 1 addition & 1 deletion src/cargo/ops/cargo_install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ fn path_source<'a>(source_id: &SourceId, config: &'a Config) -> CargoResult<Path
Ok(PathSource::new(&path, source_id, config))
}

fn select_pkg<'a, T>(
pub fn select_pkg<'a, T>(
mut source: T,
name: Option<&str>,
vers: Option<&str>,
Expand Down
2 changes: 2 additions & 0 deletions src/cargo/ops/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub use self::cargo_add::{add};
pub use self::cargo_clean::{clean, CleanOptions};
pub use self::cargo_compile::{compile, compile_with_exec, compile_ws, CompileOptions};
pub use self::cargo_compile::{CompileFilter, FilterRule, Packages};
Expand All @@ -23,6 +24,7 @@ pub use self::resolve::{add_overrides, get_resolved_packages, resolve_with_previ
pub use self::cargo_output_metadata::{output_metadata, ExportInfo, OutputMetadataOptions};
pub use self::fix::{fix, FixOptions, fix_maybe_exec_rustc};

mod cargo_add;
mod cargo_clean;
mod cargo_compile;
mod cargo_doc;
Expand Down
106 changes: 106 additions & 0 deletions src/cargo/util/toml/dependency.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
use toml_edit;

#[derive(Debug, Hash, PartialEq, Eq, Clone)]
enum DependencySource {
Version(String),
Git(String),
Path(String),
}

/// A dependency handled by Cargo
#[derive(Debug, Hash, PartialEq, Eq, Clone)]
pub struct Dependency {
/// The name of the dependency (as it is set in its `Cargo.toml` and known to crates.io)
pub name: String,
optional: bool,
source: DependencySource,
}

impl Default for Dependency {
fn default() -> Dependency {
Dependency {
name: "".into(),
optional: false,
source: DependencySource::Version("0.1.0".into()),
}
}
}

impl Dependency {
/// Create a new dependency with a name
pub fn new(name: &str) -> Dependency {
Dependency {
name: name.into(),
..Dependency::default()
}
}

/// Set dependency to a given version
pub fn set_version(mut self, version: &str) -> Dependency {
self.source = DependencySource::Version(version.into());
self
}

/// Set dependency to a given repository
pub fn set_git(mut self, repo: &str) -> Dependency {
self.source = DependencySource::Git(repo.into());
self
}

/// Set dependency to a given path
pub fn set_path(mut self, path: &str) -> Dependency {
self.source = DependencySource::Path(path.into());
self
}

/// Set whether the dependency is optional
pub fn set_optional(mut self, opt: bool) -> Dependency {
self.optional = opt;
self
}

/// Get version of dependency
pub fn version(&self) -> Option<&str> {
if let DependencySource::Version(ref version) = self.source {
Some(version)
} else {
None
}
}

/// Convert dependency to TOML
///
/// Returns a tuple with the dependency's name and either the version as a `String`
/// or the path/git repository as an `InlineTable`.
/// (If the dependency is set as `optional`, an `InlineTable` is returned in any case.)
pub fn to_toml(&self) -> (String, toml_edit::Item) {
let data: toml_edit::Item = match (self.optional, self.source.clone()) {
// Extra short when version flag only
(false, DependencySource::Version(v)) => toml_edit::value(v),
// Other cases are represented as an inline table
(optional, source) => {
let mut data = toml_edit::InlineTable::default();

match source {
DependencySource::Version(v) => {
data.get_or_insert("version", v);
}
DependencySource::Git(v) => {
data.get_or_insert("git", v);
}
DependencySource::Path(v) => {
data.get_or_insert("path", v);
}
}
if self.optional {
data.get_or_insert("optional", optional);
}

data.fmt();
toml_edit::value(toml_edit::Value::InlineTable(data))
}
};

(self.name.clone(), data)
}
}
Loading