Skip to content

Commit

Permalink
Merge f99dd40 into 113a190
Browse files Browse the repository at this point in the history
  • Loading branch information
killercup committed Oct 18, 2015
2 parents 113a190 + f99dd40 commit c5a3226
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 63 deletions.
79 changes: 35 additions & 44 deletions src/bin/add/args.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,31 @@
///! Handle `cargo add` arguments

use std::collections::BTreeMap;
use std::error::Error;
use toml;
use semver;
use std::error::Error;
use cargo_edit::Dependency;
use fetch_version::get_latest_version;

macro_rules! toml_table {
($($key:expr => $value:expr),+) => {
{
let mut dep = BTreeMap::new();
$(dep.insert(String::from($key), $value);)+
toml::Value::Table(dep)
}
}
}

/// Errors when parsing CLI arguments
quick_error! {
#[derive(Debug)]
pub enum ArgParseError {
/// Internal error parsing args to TOML
BuildingDependency {
description("Error build a dependency description from arguments")
}
}
}

#[derive(Debug, RustcDecodable)]
/// Docopts input args.
pub struct Args {
Expand Down Expand Up @@ -48,19 +67,21 @@ impl Args {
return parse_crate_name_with_version(&self.arg_crate);
}

let version = if let Some(ref version) = self.flag_ver {
parse_semver(version.clone())
let dependency = Dependency::new(&self.arg_crate);

let dependency = if let Some(ref version) = self.flag_ver {
try!(semver::VersionReq::parse(&version));
dependency.set_version(version)
} else if let Some(ref repo) = self.flag_git {
parse_git(repo.clone())
dependency.set_git(repo)
} else if let Some(ref path) = self.flag_path {
parse_path(path.clone())
dependency.set_path(path)
} else {
get_latest_version(&self.arg_crate)
.map(toml::Value::String)
.map_err(From::from)
let v = try!(get_latest_version(&self.arg_crate));
dependency.set_version(&v)
};

version.map(|data| (self.arg_crate.clone(), data))
Ok(dependency)
}
}

Expand All @@ -80,51 +101,21 @@ impl Default for Args {
}
}

macro_rules! toml_table {
($key:expr => $value:expr) => {
{
let mut dep = BTreeMap::new();
dep.insert(String::from($key), toml::Value::String($value));
toml::Value::Table(dep)
}
}
}

fn crate_name_has_version(name: &str) -> bool {
name.contains("@")
}

fn parse_crate_name_with_version(name: &str) -> Result<Dependency, Box<Error>> {
// if !crate_name_has_version(name) {
// return Err("fuu");
// }

let xs: Vec<&str> = name.splitn(2, "@").collect();
let (name, version) = (xs[0], xs[1]);
let version = try!(parse_semver(version.to_owned()));

Ok((name.to_owned(), version))
}

/// Parse (and validate) a version requirement to the correct TOML data.
fn parse_semver(version: String) -> Result<toml::Value, Box<Error>> {
try!(semver::VersionReq::parse(&version));
Ok(toml::Value::String(version))
}

/// Parse a git source to the correct TOML data.
fn parse_git(repo: String) -> Result<toml::Value, Box<Error>> {
Ok(toml_table!("git" => repo))
}

/// Parse a path to the correct TOML data.
fn parse_path(path: String) -> Result<toml::Value, Box<Error>> {
Ok(toml_table!("path" => path))
Ok(Dependency::new(name).set_version(version))
}

#[cfg(test)]
mod tests {
use toml;
use cargo_edit::Dependency;
use super::*;

#[test]
Expand All @@ -136,6 +127,6 @@ mod tests {
};

assert_eq!(args.parse_dependency().unwrap(),
("demo".to_owned(), toml::Value::String("0.4.2".to_owned())));
Dependency::new("demo").set_version("0.4.2"));
}
}
102 changes: 102 additions & 0 deletions src/dependency.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use std::collections::BTreeMap;
use toml;

macro_rules! toml_table {
($($key:expr => $value:expr),+) => {
{
let mut dep = BTreeMap::new();
$(dep.insert(String::from($key), $value);)+
toml::Value::Table(dep)
}
}
}

#[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
}

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

match source {
DependencySource::Version(v) => {
data.insert("version".into(), toml::Value::String(v));
}
DependencySource::Git(v) => {
data.insert("git".into(), toml::Value::String(v));
}
DependencySource::Path(v) => {
data.insert("path".into(), toml::Value::String(v));
}
}
data.insert("optional".into(), toml::Value::Boolean(optional));

toml::Value::Table(data)
}
};

(self.name.clone(), data)
}
}
4 changes: 3 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,7 @@ extern crate quick_error;
extern crate toml;

mod manifest;
mod dependency;

pub use manifest::{Dependency, Manifest};
pub use dependency::Dependency;
pub use manifest::Manifest;
33 changes: 15 additions & 18 deletions src/manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use toml;

/// A Crate Dependency
pub type Dependency = (String, toml::Value);
use dependency::Dependency;

/// Enumeration of errors which can occur when working with a rust manifest.
quick_error! {
Expand Down Expand Up @@ -152,8 +151,9 @@ impl Manifest {
#[cfg_attr(feature = "dev", allow(toplevel_ref_arg))]
pub fn insert_into_table(&mut self,
table: &str,
&(ref name, ref data): &Dependency)
dep: &Dependency)
-> Result<(), ManifestError> {
let (ref name, ref data) = dep.to_toml();
let ref mut manifest = self.data;
let entry = manifest.entry(String::from(table))
.or_insert(toml::Value::Table(BTreeMap::new()));
Expand All @@ -174,14 +174,14 @@ impl Manifest {
/// # extern crate cargo_edit;
/// # extern crate toml;
/// # fn main() {
/// use cargo_edit::Manifest;
/// use cargo_edit::{Dependency, Manifest};
/// use toml;
///
/// let mut manifest = Manifest { data: toml::Table::new() };
/// let dep = ("cargo-edit".to_owned(), toml::Value::String("0.1.0".to_owned()));
/// let dep = Dependency::new("cargo-edit").set_version("0.1.0");
/// let _ = manifest.insert_into_table("dependencies", &dep);
/// assert!(manifest.remove_from_table("dependencies", &dep.0).is_ok());
/// assert!(manifest.remove_from_table("dependencies", &dep.0).is_err());
/// assert!(manifest.remove_from_table("dependencies", &dep.name).is_ok());
/// assert!(manifest.remove_from_table("dependencies", &dep.name).is_err());
/// # }
/// ```
#[cfg_attr(feature = "dev", allow(toplevel_ref_arg))]
Expand Down Expand Up @@ -232,6 +232,7 @@ impl str::FromStr for Manifest {
#[cfg(test)]
mod tests {
use super::*;
use dependency::Dependency;
use std::collections::BTreeMap;
use toml;

Expand All @@ -243,30 +244,26 @@ mod tests {
let mut copy = Manifest { data: toml::Table::new() };
copy.data.insert("dependencies".to_owned(),
toml::Value::Table(BTreeMap::new()));
let dep = ("cargo-edit".to_owned(),
toml::Value::String("0.1.0".to_owned()));
let dep = Dependency::new("cargo-edit").set_version("0.1.0");
let _ = manifest.insert_into_table("dependencies", &dep);
assert!(manifest.remove_from_table("dependencies", &dep.0).is_ok());
assert!(manifest.remove_from_table("dependencies", &dep.name).is_ok());
assert_eq!(manifest, copy);
}

#[test]
fn remove_dependency_no_section() {
let mut manifest = Manifest { data: toml::Table::new() };
let dep = ("cargo-edit".to_owned(),
toml::Value::String("0.1.0".to_owned()));
assert!(manifest.remove_from_table("dependencies", &dep.0).is_err());
let dep = Dependency::new("cargo-edit").set_version("0.1.0");
assert!(manifest.remove_from_table("dependencies", &dep.name).is_err());
}

#[test]
fn remove_dependency_non_existent() {
let mut manifest = Manifest { data: toml::Table::new() };
let dep = ("cargo-edit".to_owned(),
toml::Value::String("0.1.0".to_owned()));
let other_dep = ("other-dep".to_owned(),
toml::Value::String("0.1.0".to_owned()));
let dep = Dependency::new("cargo-edit").set_version("0.1.0");
let other_dep = Dependency::new("other-dep").set_version("0.1.0");
let _ = manifest.insert_into_table("dependencies", &other_dep);
assert!(manifest.remove_from_table("dependencies", &dep.0).is_err());
assert!(manifest.remove_from_table("dependencies", &dep.name).is_err());

}
}

0 comments on commit c5a3226

Please sign in to comment.