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

Add a way to configure the format of the version string #436

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all 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
25 changes: 17 additions & 8 deletions src/bin/add/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use cargo_edit::{find, registry_url, Dependency};
use cargo_edit::{get_latest_dependency, CrateName};
use cargo_edit::config::format_version;
use std::path::PathBuf;
use structopt::StructOpt;

Expand Down Expand Up @@ -166,7 +167,7 @@ impl Args {
}
}

fn parse_single_dependency(&self, crate_name: &str) -> Result<Dependency> {
fn parse_single_dependency(&self, crate_name: &str, version_fmt: &Option<String>) -> Result<Dependency> {
let crate_name = CrateName::new(crate_name);

if let Some(mut dependency) = crate_name.parse_as_version()? {
Expand All @@ -181,6 +182,11 @@ impl Args {
dependency = dependency.set_path(path.to_str().unwrap());
}

if let Some(ref version) = dependency.version() {
let version = format_version(version, version_fmt);
return Ok(dependency.set_version(&version));
}

Ok(dependency)
} else if crate_name.is_url_or_path() {
Ok(crate_name.parse_crate_name_from_uri()?)
Expand All @@ -199,7 +205,9 @@ impl Args {
dependency = dependency.set_path(path.to_str().unwrap());
}
if let Some(version) = &self.vers {
dependency = dependency.set_version(parse_version_req(version)?);
let version = parse_version_req(version)?;
let version = format_version(&version, &version_fmt);
dependency = dependency.set_version(&version);
}
let registry_url = if let Some(registry) = &self.registry {
Some(registry_url(&find(&self.manifest_path)?, Some(registry))?)
Expand All @@ -221,6 +229,7 @@ impl Args {
// returned `Err(FetchVersionError::GetVersion)`
version = dep.version().unwrap_or_else(|| unreachable!())
);
let v = format_version(&v, &version_fmt);
dependency = dep.set_version(&v);
}

Expand All @@ -234,7 +243,7 @@ impl Args {
}

/// Build dependencies from arguments
pub fn parse_dependencies(&self) -> Result<Vec<Dependency>> {
pub fn parse_dependencies(&self, version_fmt: &Option<String>) -> Result<Vec<Dependency>> {
if self.crates.len() > 1
&& (self.git.is_some() || self.path.is_some() || self.vers.is_some())
{
Expand All @@ -252,7 +261,7 @@ impl Args {
self.crates
.iter()
.map(|crate_name| {
self.parse_single_dependency(crate_name).map(|x| {
self.parse_single_dependency(crate_name, version_fmt).map(|x| {
let mut x = x
.set_optional(self.optional)
.set_features(self.features.clone())
Expand Down Expand Up @@ -319,7 +328,7 @@ mod tests {
};

assert_eq!(
args.parse_dependencies().unwrap(),
args.parse_dependencies(&None).unwrap(),
vec![Dependency::new("demo").set_version("0.4.2")]
);
}
Expand All @@ -333,7 +342,7 @@ mod tests {
..Args::default()
};
assert_eq!(
args_github.parse_dependencies().unwrap(),
args_github.parse_dependencies(&None).unwrap(),
vec![Dependency::new("cargo-edit").set_git(github_url, None)]
);

Expand All @@ -343,7 +352,7 @@ mod tests {
..Args::default()
};
assert_eq!(
args_gitlab.parse_dependencies().unwrap(),
args_gitlab.parse_dependencies(&None).unwrap(),
vec![Dependency::new("polly").set_git(gitlab_url, None)]
);
}
Expand All @@ -356,7 +365,7 @@ mod tests {
..Args::default()
};
assert_eq!(
args_path.parse_dependencies().unwrap(),
args_path.parse_dependencies(&None).unwrap(),
vec![Dependency::new("cargo-edit").set_path(self_path)]
);
}
Expand Down
12 changes: 10 additions & 2 deletions src/bin/add/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ extern crate error_chain;

use crate::args::{Args, Command};
use cargo_edit::{
find, manifest_from_pkgid, registry_url, update_registry_index, Dependency, Manifest,
config, find, manifest_from_pkgid, registry_url, update_registry_index, Dependency, Manifest,
};
use std::borrow::Cow;
use std::io::Write;
Expand Down Expand Up @@ -121,7 +121,15 @@ fn handle_add(args: &Args) -> Result<()> {
Cow::Borrowed(&args.manifest_path)
};
let mut manifest = Manifest::open(&manifest_path)?;
let deps = &args.parse_dependencies()?;
let search_path = if let Some(ref path) = *manifest_path {
Cow::Borrowed(path)
} else {
Cow::Owned(std::env::current_dir().unwrap())
};
let version_fmt = config::get(&*search_path, "add.version_fmt")
.or_else(|| config::get(&*search_path, "version_fmt"))
.and_then(|fmt| fmt.as_str().map(|s| s.to_string()));
let deps = &args.parse_dependencies(&version_fmt)?;

if !args.offline && std::env::var("CARGO_IS_TEST").is_err() {
let url = registry_url(
Expand Down
12 changes: 10 additions & 2 deletions src/bin/upgrade/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ extern crate error_chain;

use crate::errors::*;
use cargo_edit::{
find, get_latest_dependency, manifest_from_pkgid, registry_url, update_registry_index,
config::{self, format_version}, find, get_latest_dependency, manifest_from_pkgid, registry_url, update_registry_index,
CrateName, Dependency, LocalManifest,
};
use failure::Fail;
Expand Down Expand Up @@ -297,8 +297,12 @@ impl Manifests {
for (mut manifest, package) in self.0 {
println!("{}:", package.name);

let version_fmt = config::get(&manifest.path, "upgrade.version_fmt")
.or_else(|| config::get(&manifest.path, "version_fmt"))
.and_then(|fmt| fmt.as_str().map(|s| s.to_string()));
for (dep, version) in &upgraded_deps.0 {
let mut new_dep = Dependency::new(&dep.name).set_version(version);
let version = format_version(&version, &version_fmt);
let mut new_dep = Dependency::new(&dep.name).set_version(&version);
if let Some(rename) = dep.rename() {
new_dep = new_dep.set_rename(&rename);
}
Expand Down Expand Up @@ -340,6 +344,9 @@ impl Manifests {
for (mut manifest, package) in self.0 {
println!("{}:", package.name);

let version_fmt = config::get(&manifest.path, "upgrade.version_fmt")
.or_else(|| config::get(&manifest.path, "version_fmt"))
.and_then(|fmt| fmt.as_str().map(|s| s.to_string()));
// Upgrade the manifests one at a time, as multiple manifests may
// request the same dependency at differing versions.
for (name, version) in package
Expand All @@ -358,6 +365,7 @@ impl Manifests {
None
})
{
let version = format_version(&version, &version_fmt);
manifest.upgrade(
&Dependency::new(&name).set_version(&version),
dry_run,
Expand Down
85 changes: 85 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
//! Module containing some helpers for accessing cargo config

use std::path::{Path, PathBuf};
use crate::errors::*;

/// Iterates over configs starting with the one closest to the work_dir and ending
/// with the cargo_home config.
pub fn configs<'a>(work_dir: &'a Path) -> impl Iterator<Item=PathBuf> + 'a {
let home = match cargo_home() {
Ok(home) => if home.is_file() {
Some(home)
} else {
None
},
Err(..) => None,
};
work_dir.ancestors().filter_map(|dir| {
let path = dir.join(".cargo").join("config");
if path.is_file() {
Some(path)
} else {
None
}
}).chain(home.map(|dir| dir.join("config")))
}

/// Returns the location of the cargo home directory, if possible
pub fn cargo_home() -> Result<PathBuf> {
let default_cargo_home = dirs::home_dir()
.map(|x| x.join(".cargo"))
.chain_err(|| ErrorKind::ReadHomeDirFailure)?;
let cargo_home = std::env::var("CARGO_HOME")
.map(PathBuf::from)
.unwrap_or(default_cargo_home);
Ok(cargo_home)
}

/// Takes a version string and a format string and if possible,
/// formats the version according to the format string. Currently accepts
/// 3 placeholders in the format string, `{MAJOR}`, `{MINOR}`, and `{PATCH}`
pub fn format_version(version: &str, fmt: &Option<String>) -> String {
let fmt = if let Some(ref fmt) = fmt {
fmt
} else {
return version.into();
};
let version = if let Ok(version) = semver::Version::parse(version) {
version
} else {
return version.into();
};
fmt.replace("{MAJOR}", &version.major.to_string())
.replace("{MINOR}", &version.minor.to_string())
.replace("{PATCH}", &version.patch.to_string())
}

/// Allows a user to get a config value inside the `[cargo-edit]` config table of
/// a .cargo/config file. All keys are relative to this table, so calling this like
///
/// ```rust,ignore
/// config::get(manifest_path, "add.version_fmt")
/// ```
///
/// will look for a value that looks like this in the config file:
///
/// ```toml,ignore
/// [cargo-edit.add]
/// version_fmt = "{major}.{minor}"
/// ```
pub fn get(manifest_path: impl AsRef<Path>, key: &str) -> Option<toml::Value> {
'outer: for config in configs(manifest_path.as_ref()) {
let content = std::fs::read(config).ok()?;
let config = toml::from_slice::<toml::Value>(&content).ok()?;
let mut obj = config.get("cargo-edit")?;
for part in key.split('.') {
if let Some(val) = obj.get(part) {
obj = val;
} else {
continue 'outer;
};
}
return Some(obj.clone());
}
None
}
20 changes: 15 additions & 5 deletions src/fetch.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::errors::*;
use crate::registry::{registry_path, registry_path_from_url};
use crate::{Dependency, Manifest};
use crate::config::{self, format_version};
use regex::Regex;
use std::env;
use std::io::Write;
Expand Down Expand Up @@ -46,6 +47,10 @@ pub fn get_latest_dependency(
}
};

let version_fmt = config::get(manifest_path, "add.version_fmt")
.or_else(|| config::get(manifest_path, "version_fmt"))
.and_then(|fmt| fmt.as_str().map(|s| s.to_string()));
let new_version = format_version(&new_version, &version_fmt);
return Ok(Dependency::new(crate_name).set_version(&new_version));
}

Expand All @@ -60,7 +65,7 @@ pub fn get_latest_dependency(

let crate_versions = fuzzy_query_registry_index(crate_name, &registry_path)?;

let dep = read_latest_version(&crate_versions, flag_allow_prerelease)?;
let dep = read_latest_version(&crate_versions, &manifest_path, flag_allow_prerelease)?;

if dep.name != crate_name {
println!("WARN: Added `{}` instead of `{}`", dep.name, crate_name);
Expand All @@ -77,6 +82,7 @@ fn version_is_stable(version: &CrateVersion) -> bool {
/// Read latest version from Versions structure
fn read_latest_version(
versions: &[CrateVersion],
manifest_path: &Path,
flag_allow_prerelease: bool,
) -> Result<Dependency> {
let latest = versions
Expand All @@ -88,6 +94,10 @@ fn read_latest_version(

let name = &latest.name;
let version = latest.version.to_string();
let version_fmt = config::get(manifest_path, "add.version_fmt")
.or_else(|| config::get(manifest_path, "version_fmt"))
.and_then(|fmt| fmt.as_str().map(|s| s.to_string()));
let version = format_version(&version, &version_fmt);
Ok(Dependency::new(name).set_version(&version))
}

Expand Down Expand Up @@ -177,7 +187,7 @@ fn get_latest_stable_version_from_json() {
.expect("crate version is correctly parsed");

assert_eq!(
read_latest_version(&versions, false)
read_latest_version(&versions, &std::env::current_dir().unwrap(), false)
.unwrap()
.version()
.unwrap(),
Expand All @@ -204,7 +214,7 @@ fn get_latest_unstable_or_stable_version_from_json() {
.expect("crate version is correctly parsed");

assert_eq!(
read_latest_version(&versions, true)
read_latest_version(&versions, &std::env::current_dir().unwrap(), true)
.unwrap()
.version()
.unwrap(),
Expand All @@ -231,7 +241,7 @@ fn get_latest_version_from_json_test() {
.expect("crate version is correctly parsed");

assert_eq!(
read_latest_version(&versions, false)
read_latest_version(&versions, &std::env::current_dir().unwrap(), false)
.unwrap()
.version()
.unwrap(),
Expand All @@ -257,7 +267,7 @@ fn get_no_latest_version_from_json_when_all_are_yanked() {
)
.expect("crate version is correctly parsed");

assert!(read_latest_version(&versions, false).is_err());
assert!(read_latest_version(&versions, &std::env::current_dir().unwrap(), false).is_err());
}

/// Gets the checkedout branch name of .cargo/registry/index/gh.neting.cc-*/.git/refs or
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ extern crate error_chain;
#[macro_use]
extern crate serde_derive;

pub mod config;
mod crate_name;
mod dependency;
mod errors;
Expand Down
27 changes: 3 additions & 24 deletions src/registry.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use self::code_from_cargo::Kind;
use crate::errors::*;
use crate::config::{self, cargo_home};
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use url::Url;
Expand Down Expand Up @@ -38,16 +39,6 @@ struct CargoConfig {
source: HashMap<String, Source>,
}

fn cargo_home() -> Result<PathBuf> {
let default_cargo_home = dirs::home_dir()
.map(|x| x.join(".cargo"))
.chain_err(|| ErrorKind::ReadHomeDirFailure)?;
let cargo_home = std::env::var("CARGO_HOME")
.map(PathBuf::from)
.unwrap_or(default_cargo_home);
Ok(cargo_home)
}

/// Find the URL of a registry
pub fn registry_url(manifest_path: &Path, registry: Option<&str>) -> Result<Url> {
// TODO support local registry sources, directory sources, git sources: https://doc.rust-lang.org/cargo/reference/source-replacement.html?highlight=replace-with#source-replacement
Expand All @@ -72,20 +63,8 @@ pub fn registry_url(manifest_path: &Path, registry: Option<&str>) -> Result<Url>
// put relations in this map.
let mut registries: HashMap<String, Source> = HashMap::new();
// ref: https://doc.rust-lang.org/cargo/reference/config.html#hierarchical-structure
for work_dir in manifest_path
.parent()
.expect("there must be a parent directory")
.ancestors()
{
let config_path = work_dir.join(".cargo").join("config");
if config_path.is_file() {
read_config(&mut registries, config_path)?;
}
}

let default_config_path = cargo_home()?.join("config");
if default_config_path.is_file() {
read_config(&mut registries, default_config_path)?;
for config_path in config::configs(&manifest_path.parent().expect("there must be a parent directory")) {
read_config(&mut registries, config_path)?;
}

// find head of the relevant linked list
Expand Down