summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas O'Donnell <andytom@users.noreply.github.com>2021-08-06 21:16:14 +0200
committerGitHub <noreply@github.com>2021-08-06 21:16:14 +0200
commitb7b7df98854b2d4ff9c5f2fdddc796449cdab908 (patch)
treec80814bf0e41351c48892981c0b931f588850698
parent3ea425318bd262c287db9346831d41422e976fb8 (diff)
feat(package): Add ability to format the version (#2959)
Have added the ability to use format the version of the package using the `version_format` option. While doing this I have also done some refactoring of the module to remove the if/else if/... block and replace it with an iterator. This should make fix some edge cases where versions are not correctly picked up due to other files an example would be a python project that has a `pyproject.toml` file but using the `setup.cfg` for the package version. It should also make it easier to make the order of the list configurable in the future.
-rw-r--r--docs/config/README.md15
-rw-r--r--src/configs/package.rs2
-rw-r--r--src/modules/package.rs267
3 files changed, 147 insertions, 137 deletions
diff --git a/docs/config/README.md b/docs/config/README.md
index e4fe6b1de..f32c0b231 100644
--- a/docs/config/README.md
+++ b/docs/config/README.md
@@ -2139,13 +2139,14 @@ package, and shows its current version. The module currently supports `npm`, `ni
### Options
-| Option | Default | Description |
-| ----------------- | ---------------------------------- | ---------------------------------------------------------- |
-| `format` | `"is [$symbol$version]($style) "` | The format for the module. |
-| `symbol` | `"📦 "` | The symbol used before displaying the version the package. |
-| `style` | `"bold 208"` | The style for the module. |
-| `display_private` | `false` | Enable displaying version for packages marked as private. |
-| `disabled` | `false` | Disables the `package` module. |
+| Option | Default | Description |
+| ----------------- | ---------------------------------- | ------------------------------------------------------------------------ |
+| `format` | `"is [$symbol$version]($style) "` | The format for the module. |
+| `symbol` | `"📦 "` | The symbol used before displaying the version the package. |
+| `version_format` | `"v${raw}"` | The version format. Available vars are `raw`, `major`, `minor`, & `patch`|
+| `style` | `"bold 208"` | The style for the module. |
+| `display_private` | `false` | Enable displaying version for packages marked as private. |
+| `disabled` | `false` | Disables the `package` module. |
### Variables
diff --git a/src/configs/package.rs b/src/configs/package.rs
index 7fe422155..887cda0d0 100644
--- a/src/configs/package.rs
+++ b/src/configs/package.rs
@@ -10,6 +10,7 @@ pub struct PackageConfig<'a> {
pub style: &'a str,
pub display_private: bool,
pub disabled: bool,
+ pub version_format: &'a str,
}
impl<'a> Default for PackageConfig<'a> {
@@ -20,6 +21,7 @@ impl<'a> Default for PackageConfig<'a> {
style: "208 bold",
display_private: false,
disabled: false,
+ version_format: "v${raw}",
}
}
}
diff --git a/src/modules/package.rs b/src/modules/package.rs
index c69e1e3cd..4cc52c533 100644
--- a/src/modules/package.rs
+++ b/src/modules/package.rs
@@ -1,6 +1,6 @@
use super::{Context, Module, RootModuleConfig};
use crate::configs::package::PackageConfig;
-use crate::formatter::StringFormatter;
+use crate::formatter::{StringFormatter, VersionFormatter};
use crate::utils;
use ini::Ini;
@@ -15,7 +15,7 @@ use serde_json as json;
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
let mut module = context.new_module("package");
let config: PackageConfig = PackageConfig::try_load(module.config);
- let module_version = get_package_version(context, &config)?;
+ let module_version = get_version(context, &config)?;
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
formatter
@@ -45,28 +45,12 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
Some(module)
}
-fn extract_cargo_version(file_contents: &str) -> Option<String> {
- let cargo_toml: toml::Value = toml::from_str(file_contents).ok()?;
- let raw_version = cargo_toml.get("package")?.get("version")?.as_str()?;
-
- let formatted_version = format_version(raw_version);
- Some(formatted_version)
-}
-
-fn extract_nimble_version(context: &Context) -> Option<String> {
- let cmd_output = context.exec_cmd("nimble", &["dump", "--json"])?;
-
- let nimble_json: json::Value = json::from_str(&cmd_output.stdout).ok()?;
- let raw_version = nimble_json.get("version")?.as_str()?;
-
- let formatted_version = format_version(raw_version);
- Some(formatted_version)
-}
-
-fn extract_package_version(file_contents: &str, display_private: bool) -> Option<String> {
- let package_json: json::Value = json::from_str(file_contents).ok()?;
+fn get_node_package_version(context: &Context, config: &PackageConfig) -> Option<String> {
+ let file_contents = utils::read_file(&context.current_dir.join("package.json")).ok()?;
+ let package_json: json::Value = json::from_str(&file_contents).ok()?;
- if !display_private && package_json.get("private").and_then(json::Value::as_bool) == Some(true)
+ if !config.display_private
+ && package_json.get("private").and_then(json::Value::as_bool) == Some(true)
{
return None;
}
@@ -76,7 +60,7 @@ fn extract_package_version(file_contents: &str, display_private: bool) -> Option
return None;
};
- let formatted_version = format_version(raw_version);
+ let formatted_version = format_version(raw_version, config.version_format)?;
if formatted_version == "v0.0.0-development" || formatted_version.starts_with("v0.0.0-semantic")
{
return Some("semantic".to_string());
@@ -85,69 +69,69 @@ fn extract_package_version(file_contents: &str, display_private: bool) -> Option
Some(formatted_version)
}
-fn extract_poetry_version(file_contents: &str) -> Option<String> {
- let poetry_toml: toml::Value = toml::from_str(file_contents).ok()?;
+fn get_poetry_version(context: &Context, config: &PackageConfig) -> Option<String> {
+ let file_contents = utils::read_file(&context.current_dir.join("pyproject.toml")).ok()?;
+ let poetry_toml: toml::Value = toml::from_str(&file_contents).ok()?;
let raw_version = poetry_toml
.get("tool")?
.get("poetry")?
.get("version")?
.as_str()?;
- let formatted_version = format_version(raw_version);
- Some(formatted_version)
+ format_version(raw_version, config.version_format)
}
-fn extract_setup_cfg_version(file_contents: &str) -> Option<String> {
- let ini = Ini::load_from_str(file_contents).ok()?;
+fn get_setup_cfg_version(context: &Context, config: &PackageConfig) -> Option<String> {
+ let file_contents = utils::read_file(context.current_dir.join("setup.cfg")).ok()?;
+ let ini = Ini::load_from_str(&file_contents).ok()?;
let raw_version = ini.get_from(Some("metadata"), "version")?;
- let formatted_version = format_version(raw_version);
- Some(formatted_version)
+ format_version(raw_version, config.version_format)
}
-fn extract_gradle_version(file_contents: &str) -> Option<String> {
+fn get_gradle_version(context: &Context, config: &PackageConfig) -> Option<String> {
+ let file_contents = utils::read_file(context.current_dir.join("build.gradle")).ok()?;
let re = Regex::new(r#"(?m)^version ['"](?P<version>[^'"]+)['"]$"#).unwrap();
- let caps = re.captures(file_contents)?;
+ let caps = re.captures(&file_contents)?;
- let formatted_version = format_version(&caps["version"]);
- Some(formatted_version)
+ format_version(&caps["version"], config.version_format)
}
-fn extract_composer_version(file_contents: &str) -> Option<String> {
- let composer_json: json::Value = json::from_str(file_contents).ok()?;
+fn get_composer_version(context: &Context, config: &PackageConfig) -> Option<String> {
+ let file_contents = utils::read_file(context.current_dir.join("composer.json")).ok()?;
+ let composer_json: json::Value = json::from_str(&file_contents).ok()?;
let raw_version = composer_json.get("version")?.as_str()?;
- if raw_version == "null" {
- return None;
- };
- let formatted_version = format_version(raw_version);
- Some(formatted_version)
+ format_version(raw_version, config.version_format)
}
-fn extract_project_version(file_contents: &str) -> Option<String> {
- let project_toml: toml::Value = toml::from_str(file_contents).ok()?;
+fn get_julia_project_version(context: &Context, config: &PackageConfig) -> Option<String> {
+ let file_contents = utils::read_file(context.current_dir.join("Project.toml")).ok()?;
+ let project_toml: toml::Value = toml::from_str(&file_contents).ok()?;
let raw_version = project_toml.get("version")?.as_str()?;
- let formatted_version = format_version(raw_version);
- Some(formatted_version)
+ format_version(raw_version, config.version_format)
}
-fn extract_helm_package_version(file_contents: &str) -> Option<String> {
- let yaml = yaml_rust::YamlLoader::load_from_str(file_contents).ok()?;
+fn get_helm_package_version(context: &Context, config: &PackageConfig) -> Option<String> {
+ let file_contents = utils::read_file(context.current_dir.join("Chart.yaml")).ok()?;
+ let yaml = yaml_rust::YamlLoader::load_from_str(&file_contents).ok()?;
let version = yaml.first()?["version"].as_str()?;
- Some(format_version(version))
+
+ format_version(version, config.version_format)
}
-fn extract_mix_version(file_contents: &str) -> Option<String> {
+fn get_mix_version(context: &Context, config: &PackageConfig) -> Option<String> {
+ let file_contents = utils::read_file(context.current_dir.join("mix.exs")).ok()?;
let re = Regex::new(r#"(?m)version: "(?P<version>[^"]+)""#).unwrap();
- let caps = re.captures(file_contents)?;
+ let caps = re.captures(&file_contents)?;
- let formatted_version = format_version(&caps["version"]);
- Some(formatted_version)
+ format_version(&caps["version"], config.version_format)
}
-fn extract_maven_version(file_contents: &str) -> Option<String> {
- let mut reader = QXReader::from_str(file_contents);
+fn get_maven_version(context: &Context, config: &PackageConfig) -> Option<String> {
+ let pom_file = utils::read_file(context.current_dir.join("pom.xml")).ok()?;
+ let mut reader = QXReader::from_str(&pom_file);
reader.trim_text(true);
let mut buf = vec![];
@@ -167,7 +151,7 @@ fn extract_maven_version(file_contents: &str) -> Option<String> {
let ver = t.unescape_and_decode(&reader).ok();
return match ver {
// Ignore version which is just a property reference
- Some(ref v) if !v.starts_with('$') => ver,
+ Some(ref v) if !v.starts_with('$') => format_version(v, config.version_format),
_ => None,
};
}
@@ -184,80 +168,88 @@ fn extract_maven_version(file_contents: &str) -> Option<String> {
None
}
-fn extract_meson_version(file_contents: &str) -> Option<String> {
- let file_contents = file_contents.split_ascii_whitespace().collect::<String>();
+fn get_meson_version(context: &Context, config: &PackageConfig) -> Option<String> {
+ let file_contents = utils::read_file(context.current_dir.join("meson.build"))
+ .ok()?
+ .split_ascii_whitespace()
+ .collect::<String>();
let re = Regex::new(r#"project\([^())]*,version:'(?P<version>[^']+)'[^())]*\)"#).unwrap();
let caps = re.captures(&file_contents)?;
- let formatted_version = format_version(&caps["version"]);
- Some(formatted_version)
+ format_version(&caps["version"], config.version_format)
}
-fn extract_vmod_version(file_contents: &str) -> Option<String> {
+fn get_vmod_version(context: &Context, config: &PackageConfig) -> Option<String> {
+ let file_contents = utils::read_file(context.current_dir.join("v.mod")).ok()?;
let re = Regex::new(r"(?m)^\s*version\s*:\s*'(?P<version>[^']+)'").unwrap();
- let caps = re.captures(file_contents)?;
- let formatted_version = format_version(&caps["version"]);
- Some(formatted_version)
+ let caps = re.captures(&file_contents)?;
+ format_version(&caps["version"], config.version_format)
}
-fn extract_vpkg_version(file_contents: &str) -> Option<String> {
- let vpkg_json: json::Value = json::from_str(file_contents).ok()?;
- let version = vpkg_json.get("version")?.as_str()?;
- if version == "null" {
- return None;
- }
- let formatted_version = format_version(version);
- Some(formatted_version)
+fn get_vpkg_version(context: &Context, config: &PackageConfig) -> Option<String> {
+ let file_contents = utils::read_file(context.current_dir.join("vpkg.json")).ok()?;
+ let vpkg_json: json::Value = json::from_str(&file_contents).ok()?;
+ let raw_version = vpkg_json.get("version")?.as_str()?;
+
+ format_version(raw_version, config.version_format)
}
-fn get_package_version(context: &Context, config: &PackageConfig) -> Option<String> {
- let base_dir = &context.current_dir;
+fn get_cargo_version(context: &Context, config: &PackageConfig) -> Option<String> {
+ let file_contents = utils::read_file(&context.current_dir.join("Cargo.toml")).ok()?;
- if let Ok(cargo_toml) = utils::read_file(base_dir.join("Cargo.toml")) {
- extract_cargo_version(&cargo_toml)
- } else if context
+ let cargo_toml: toml::Value = toml::from_str(&file_contents).ok()?;
+ let raw_version = cargo_toml.get("package")?.get("version")?.as_str()?;
+
+ format_version(raw_version, config.version_format)
+}
+
+fn get_nimble_version(context: &Context, config: &PackageConfig) -> Option<String> {
+ if !context
.try_begin_scan()?
.set_extensions(&["nimble"])
.is_match()
{
- extract_nimble_version(context)
- } else if let Ok(package_json) = utils::read_file(base_dir.join("package.json")) {
- extract_package_version(&package_json, config.display_private)
- } else if let Ok(poetry_toml) = utils::read_file(base_dir.join("pyproject.toml")) {
- extract_poetry_version(&poetry_toml)
- } else if let Ok(setup_cfg) = utils::read_file(base_dir.join("setup.cfg")) {
- extract_setup_cfg_version(&setup_cfg)
- } else if let Ok(composer_json) = utils::read_file(base_dir.join("composer.json")) {
- extract_composer_version(&composer_json)
- } else if let Ok(build_gradle) = utils::read_file(base_dir.join("build.gradle")) {
- extract_gradle_version(&build_gradle)
- } else if let Ok(project_toml) = utils::read_file(base_dir.join("Project.toml")) {
- extract_project_version(&project_toml)
- } else if let Ok(mix_file) = utils::read_file(base_dir.join("mix.exs")) {
- extract_mix_version(&mix_file)
- } else if let Ok(chart_file) = utils::read_file(base_dir.join("Chart.yaml")) {
- extract_helm_package_version(&chart_file)
- } else if let Ok(pom_file) = utils::read_file(base_dir.join("pom.xml")) {
- extract_maven_version(&pom_file)
- } else if let Ok(meson_build) = utils::read_file(base_dir.join("meson.build")) {
- extract_meson_version(&meson_build)
- } else if let Ok(vlang_mod) = utils::read_file(base_dir.join("v.mod")) {
- extract_vmod_version(&vlang_mod)
- } else if let Ok(vlang_vpkg) = utils::read_file(base_dir.join("vpkg.json")) {
- extract_vpkg_version(&vlang_vpkg)
- } else {
- None
- }
+ return None;
+ };
+
+ let cmd_output = context.exec_cmd("nimble", &["dump", "--json"])?;
+ let nimble_json: json::Value = json::from_str(&cmd_output.stdout).ok()?;
+
+ let raw_version = nimble_json.get("version")?.as_str()?;
+
+ format_version(raw_version, config.version_format)
}
-fn format_version(version: &str) -> String {
- let cleaned = version.replace('"', "").trim().to_string();
- if cleaned.starts_with('v') {
- cleaned
- } else {
- format!("v{}", cleaned)
- }
+fn get_version(context: &Context, config: &PackageConfig) -> Option<String> {
+ let package_version_fn: Vec<fn(&Context, &PackageConfig) -> Option<String>> = vec![
+ get_cargo_version,
+ get_nimble_version,
+ get_node_package_version,
+ get_poetry_version,
+ get_setup_cfg_version,
+ get_composer_version,
+ get_gradle_version,
+ get_julia_project_version,
+ get_mix_version,
+ get_helm_package_version,
+ get_maven_version,
+ get_meson_version,
+ get_vmod_version,
+ get_vpkg_version,
+ ];
+
+ package_version_fn.iter().find_map(|f| f(context, config))
+}
+
+fn format_version(version: &str, version_format: &str) -> Option<String> {
+ let cleaned = version
+ .replace('"', "")
+ .trim()
+ .trim_start_matches('v')
+ .to_string();
+
+ VersionFormatter::format_module_version("package", &cleaned, version_format)
}
#[cfg(test)]
@@ -272,17 +264,32 @@ mod tests {
#[test]
fn test_format_version() {
- assert_eq!(format_version("0.1.0"), "v0.1.0");
- assert_eq!(format_version(" 0.1.0 "), "v0.1.0");
- assert_eq!(format_version("0.1.0 "), "v0.1.0");
- assert_eq!(format_version(" 0.1.0"), "v0.1.0");
- assert_eq!(format_version("\"0.1.0\""), "v0.1.0");
-
- assert_eq!(format_version("v0.1.0"), "v0.1.0");
- assert_eq!(format_version(" v0.1.0 "), "v0.1.0");
- assert_eq!(format_version(" v0.1.0"), "v0.1.0");
- assert_eq!(format_version("v0.1.0 "), "v0.1.0");
- assert_eq!(format_version("\"v0.1.0\""), "v0.1.0");
+ let raw_expected = Some(String::from("v1.2.3"));
+
+ assert_eq!(format_version("1.2.3", "v${raw}"), raw_expected);
+ assert_eq!(format_version(" 1.2.3 ", "v${raw}"), raw_expected);
+ assert_eq!(format_version("1.2.3 ", "v${raw}"), raw_expected);
+ assert_eq!(format_version(" 1.2.3", "v${raw}"), raw_expected);
+ assert_eq!(format_version("\"1.2.3\"", "v${raw}"), raw_expected);
+
+ assert_eq!(format_version("v1.2.3", "v${raw}"), raw_expected);
+ assert_eq!(format_version(" v1.2.3 ", "v${raw}"), raw_expected);
+ assert_eq!(format_version(" v1.2.3", "v${raw}"), raw_expected);
+ assert_eq!(format_version("v1.2.3 ", "v${raw}"), raw_expected);
+ assert_eq!(format_version("\"v1.2.3\"", "v${raw}"), raw_expected);
+
+ let major_expected = Some(String::from("v1"));
+ assert_eq!(format_version("1.2.3", "v${major}"), major_expected);
+ assert_eq!(format_version(" 1.2.3 ", "v${major}"), major_expected);
+ assert_eq!(format_version("1.2.3 ", "v${major}"), major_expected);
+ assert_eq!(format_version(" 1.2.3", "v${major}"), major_expected);
+ assert_eq!(format_version("\"1.2.3\"", "v${major}"), major_expected);
+
+ assert_eq!(format_version("v1.2.3", "v${major}"), major_expected);
+ assert_eq!(format_version(" v1.2.3 ", "v${major}"), major_expected);
+ assert_eq!(format_version(" v1.2.3", "v${major}"), major_expected);
+ assert_eq!(format_version("v1.2.3 ", "v${major}"), major_expected);
+ assert_eq!(format_version("\"v1.2.3\"", "v${major}"), major_expected);
}
#[test]
@@ -508,7 +515,7 @@ license = "MIT"
}
#[test]
- fn test_package_version_semantic_development_version() -> io::Result<()> {
+ fn test_node_package_version_semantic_development_version() -> io::Result<()> {
let config_name = "package.json";
let config_content = json::json!({
"name": "starship",
@@ -523,7 +530,7 @@ license = "MIT"
}
#[test]
- fn test_package_version_with_semantic_other_version() -> io::Result<()> {
+ fn test_node_package_version_with_semantic_other_version() -> io::Result<()> {
let config_name = "package.json";
let config_content = json::json!({
"name": "starship",
@@ -538,7 +545,7 @@ license = "MIT"
}
#[test]
- fn test_package_version_with_non_semantic_tag() -> io::Result<()> {
+ fn test_node_package_version_with_non_semantic_tag() -> io::Result<()> {
let config_name = "package.json";
let config_content = json::json!({
"name": "starship",
@@ -804,7 +811,7 @@ end";
}
#[test]
- fn test_extract_project_version() -> io::Result<()> {
+ fn test_extract_julia_project_version() -> io::Result<()> {
let config_name = "Project.toml";
let config_content = toml::toml! {
name = "starship"
@@ -819,7 +826,7 @@ end";
}
#[test]
- fn test_extract_project_version_without_version() -> io::Result<()> {
+ fn test_extract_julia_project_version_without_version() -> io::Result<()> {
let config_name = "Project.toml";
let config_content = toml::toml! {
name = "starship"
@@ -902,7 +909,7 @@ end";
let project_dir = create_project_dir()?;
fill_config(&project_dir, "pom.xml", Some(pom))?;
- expect_output(&project_dir, Some("0.3.20-SNAPSHOT"), None);
+ expect_output(&project_dir, Some("v0.3.20-SNAPSHOT"), None);
project_dir.close()
}