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.
This commit is contained in:
parent
3ea425318b
commit
b7b7df9885
|
@ -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
|
||||
|
||||
|
|
|
@ -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}",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()?;
|
||||
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()?;
|
||||
|
||||
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()?;
|
||||
|
||||
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");
|
||||
let raw_expected = Some(String::from("v1.2.3"));
|
||||
|
||||
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");
|
||||
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()
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue