From fe030c2064e20f1359ab4844d3b3472a1ed1f6b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dario=20Vladovi=C4=87?= Date: Sat, 10 Apr 2021 19:06:25 +0200 Subject: [PATCH] refactor: propagate version formatting errors (#2566) * refactor: propagate version formatting errors * refactor: trim version formatting boilerplate * refactor(node): unwrap version formatting * docs: fix typo * docs: remove dots after `version_format` * feat: lazy version parsing * refactor(version-formatter): collect segments into string --- docs/config/README.md | 12 ++--- src/formatter/string_formatter.rs | 2 +- src/formatter/version.rs | 73 +++++++++++++++++-------------- src/modules/java.rs | 67 ++++++++++++++-------------- src/modules/nodejs.rs | 28 +++++++----- src/modules/python.rs | 47 ++++++++++---------- src/modules/ruby.rs | 32 +++++++------- src/modules/rust.rs | 69 +++++++++++++++-------------- 8 files changed, 172 insertions(+), 158 deletions(-) diff --git a/docs/config/README.md b/docs/config/README.md index 940161ede..6b9a6c132 100644 --- a/docs/config/README.md +++ b/docs/config/README.md @@ -1379,7 +1379,7 @@ By default the module will be shown if any of the following conditions are met: | Option | Default | Description | | ------------------- | --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | | `format` | `"via [${symbol}(${version} )]($style)"` | The format for the module. | -| `version_format`. | `v{raw}` | The version format. Available vars are `raw`, `major`, `minor`, & `patch` | +| `version_format` | `v{raw}` | The version format. Available vars are `raw`, `major`, `minor`, & `patch` | | `detect_extensions` | `["java", "class", "gradle", "jar", "cljs", "cljc"]` | Which extensions should trigger this module. | | `detect_files` | `["pom.xml", "build.gradle.kts", "build.sbt", ".java-version", ".deps.edn", "project.clj", "build.boot"]` | Which filenames should trigger this module. | | `detect_folders` | `[]` | Which folders should trigger this modules. | @@ -1825,9 +1825,9 @@ By default the module will be shown if any of the following conditions are met: | Option | Default | Description | | ------------------- | ------------------------------------ | ---------------------------------------------------------------------------------------------------- | | `format` | `"via [$symbol($version )]($style)"` | The format for the module. | -| `version_format`. | `v{raw}` | The version format. Available vars are `raw`, `major`, `minor`, & `patch` | +| `version_format` | `v{raw}` | The version format. Available vars are `raw`, `major`, `minor`, & `patch` | | `symbol` | `" "` | A format string representing the symbol of NodeJS. | -| `detect_extensions` | `["js", "mjs", "cjs", "ts"]` | Which extensions should trigger this moudle. | +| `detect_extensions` | `["js", "mjs", "cjs", "ts"]` | Which extensions should trigger this module. | | `detect_files` | `["package.json", ".node-version"]` | Which filenames should trigger this module. | | `detect_folders` | `["node_modules"]` | Which folders should trigger this module. | | `style` | `"bold green"` | The style for the module. | @@ -2135,7 +2135,7 @@ By default the module will be shown if any of the following conditions are met: | Option | Default | Description | | -------------------- | ------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------- | | `format` | `'via [${symbol}${pyenv_prefix}(${version} )(\($virtualenv\) )]($style)'` | The format for the module. | -| `version_format`. | `v{raw}` | The version format. Available vars are `raw`, `major`, `minor`, & `patch` | +| `version_format` | `v{raw}` | The version format. Available vars are `raw`, `major`, `minor`, & `patch` | | `symbol` | `"🐍 "` | A format string representing the symbol of Python | | `style` | `"yellow bold"` | The style for the module. | | `pyenv_version_name` | `false` | Use pyenv to get Python version | @@ -2214,7 +2214,7 @@ The module will be shown if any of the following conditions are met: | Option | Default | Description | | ------------------- | ------------------------------------ | ------------------------------------------------------------------------- | | `format` | `"via [$symbol($version )]($style)"` | The format for the module. | -| `version_format`. | `v{raw}` | The version format. Available vars are `raw`, `major`, `minor`, & `patch` | +| `version_format` | `v{raw}` | The version format. Available vars are `raw`, `major`, `minor`, & `patch` | | `symbol` | `"💎 "` | A format string representing the symbol of Ruby. | | `detect_extensions` | `["rb"]` | Which extensions should trigger this module. | | `detect_files` | `["Gemfile", ".ruby-version"]` | Which filenames should trigger this module. | @@ -2254,7 +2254,7 @@ The module will be shown if any of the following conditions are met: | Option | Default | Description | | ------------------- | ------------------------------------ | ------------------------------------------------------------------------- | | `format` | `"via [$symbol($version )]($style)"` | The format for the module. | -| `version_format`. | `v{raw}` | The version format. Available vars are `raw`, `major`, `minor`, & `patch` | +| `version_format` | `v{raw}` | The version format. Available vars are `raw`, `major`, `minor`, & `patch` | | `symbol` | `"🦀 "` | A format string representing the symbol of Rust | | `detect_extensions` | `["rs"]` | Which extensions should trigger this module. | | `detect_files` | `["Cargo.toml"]` | Which filenames should trigger this module. | diff --git a/src/formatter/string_formatter.rs b/src/formatter/string_formatter.rs index ee063ef1e..01d6bd9ac 100644 --- a/src/formatter/string_formatter.rs +++ b/src/formatter/string_formatter.rs @@ -30,7 +30,7 @@ type VariableMapType<'a> = type StyleVariableMapType<'a> = BTreeMap, StringFormatterError>>>; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum StringFormatterError { Custom(String), Parse(PestError), diff --git a/src/formatter/version.rs b/src/formatter/version.rs index 8d174218b..2b3e8aed1 100644 --- a/src/formatter/version.rs +++ b/src/formatter/version.rs @@ -1,5 +1,7 @@ use super::string_formatter::StringFormatterError; use super::StringFormatter; +use once_cell::sync::Lazy; +use std::ops::Deref; use versions::Versioning; pub struct VersionFormatter<'a> { @@ -17,26 +19,32 @@ impl<'a> VersionFormatter<'a> { Ok(Self { formatter }) } + /// Format version string using provided format + pub fn format_version( + version: &'a str, + format: &'a str, + ) -> Result { + Self::new(format).and_then(|formatter| formatter.format(version)) + } + /// Formats a version structure into a readable string - /// - /// No matter what comes in, this will return some usable string - pub fn format_version(self, version: &str) -> String { - let parsed = Versioning::new(version); + pub fn format(self, version: &'a str) -> Result { + let parsed = Lazy::new(|| Versioning::new(version)); let formatted = self .formatter .map(|variable| match variable { "raw" => Some(Ok(version.to_string())), - "major" => match parsed.as_ref() { + "major" => match parsed.deref().as_ref() { Some(Versioning::Ideal(v)) => Some(Ok(v.major.to_string())), Some(Versioning::General(v)) => Some(Ok(v.nth_lenient(0)?.to_string())), _ => None, }, - "minor" => match parsed.as_ref() { + "minor" => match parsed.deref().as_ref() { Some(Versioning::Ideal(v)) => Some(Ok(v.minor.to_string())), Some(Versioning::General(v)) => Some(Ok(v.nth_lenient(1)?.to_string())), _ => None, }, - "patch" => match parsed.as_ref() { + "patch" => match parsed.deref().as_ref() { Some(Versioning::Ideal(v)) => Some(Ok(v.patch.to_string())), Some(Versioning::General(v)) => Some(Ok(v.nth_lenient(2)?.to_string())), _ => None, @@ -44,14 +52,13 @@ impl<'a> VersionFormatter<'a> { _ => None, }) .parse(None); - match formatted { - Ok(segments) => segments + + formatted.map(|segments| { + segments .iter() .map(|segment| segment.value.as_str()) - .collect::>() - .join(""), - Err(_) => version.to_string(), - } + .collect::() + }) } } @@ -59,39 +66,37 @@ impl<'a> VersionFormatter<'a> { mod tests { use super::*; + const VERSION_FORMAT: &str = "major:${major} minor:${minor} patch:${patch} raw:${raw}"; + #[test] fn test_semver_full() { - const FORMAT_STR: &str = "major:${major} minor:${minor} patch:${patch} raw:${raw}"; - let result = VersionFormatter::new(FORMAT_STR) - .unwrap() - .format_version("1.2.3"); - assert_eq!(result, "major:1 minor:2 patch:3 raw:1.2.3"); + assert_eq!( + VersionFormatter::format_version("1.2.3", VERSION_FORMAT), + Ok("major:1 minor:2 patch:3 raw:1.2.3".to_string()) + ); } #[test] fn test_semver_partial() { - const FORMAT_STR: &str = "major:${major} minor:${minor} patch:${patch} raw:${raw}"; - let result = VersionFormatter::new(FORMAT_STR) - .unwrap() - .format_version("1.2"); - assert_eq!(result, "major:1 minor:2 patch: raw:1.2"); + assert_eq!( + VersionFormatter::format_version("1.2", VERSION_FORMAT), + Ok("major:1 minor:2 patch: raw:1.2".to_string()) + ); } #[test] fn test_general() { - const FORMAT_STR: &str = "major:${major} minor:${minor} patch:${patch} raw:${raw}"; - let result = VersionFormatter::new(FORMAT_STR) - .unwrap() - .format_version("1.2-a.3"); - assert_eq!(result, "major:1 minor:2 patch: raw:1.2-a.3"); + assert_eq!( + VersionFormatter::format_version("1.2-a.3", VERSION_FORMAT), + Ok("major:1 minor:2 patch: raw:1.2-a.3".to_string()) + ); } #[test] - fn test_mess() { - const FORMAT_STR: &str = "major:${major} minor:${minor} patch:${patch} raw:${raw}"; - let result = VersionFormatter::new(FORMAT_STR) - .unwrap() - .format_version("utter junk"); - assert_eq!(result, "major: minor: patch: raw:utter junk"); + fn test_dummy() { + assert_eq!( + VersionFormatter::format_version("dummy version", VERSION_FORMAT), + Ok("major: minor: patch: raw:dummy version".to_string()) + ); } } diff --git a/src/modules/java.rs b/src/modules/java.rs index bd555941e..158eb51ba 100644 --- a/src/modules/java.rs +++ b/src/modules/java.rs @@ -34,10 +34,7 @@ pub fn module<'a>(context: &'a Context) -> Option> { _ => None, }) .map(|variable| match variable { - "version" => { - let java_version = get_java_version(context, &config)?; - Some(Ok(java_version)) - } + "version" => get_java_version(context, &config).map(Ok), _ => None, }) .parse(None) @@ -74,19 +71,21 @@ fn get_java_version(context: &Context, config: &JavaConfig) -> Option { output.stdout }; - parse_java_version(config.version_format, &java_version) + format_java_version(&java_version, config.version_format) } -fn parse_java_version(version_format: &str, java_version: &str) -> Option { +fn format_java_version(java_version: &str, version_format: &str) -> Option { let re = Regex::new(JAVA_VERSION_PATTERN).ok()?; let captures = re.captures(java_version)?; let version = &captures["version"]; - Some( - VersionFormatter::new(version_format) - .ok()? - .format_version(version), - ) + match VersionFormatter::format_version(version, version_format) { + Ok(formatted) => Some(formatted), + Err(error) => { + log::warn!("Error formating `java` version:\n{}", error); + Some(format!("v{}", version)) + } + } } #[cfg(test)] @@ -98,106 +97,106 @@ mod tests { use std::io; #[test] - fn test_parse_java_version_openjdk() { + fn test_format_java_version_openjdk() { let java_8 = "OpenJDK 64-Bit Server VM (25.222-b10) for linux-amd64 JRE (1.8.0_222-b10), built on Jul 11 2019 10:18:43 by \"openjdk\" with gcc 4.4.7 20120313 (Red Hat 4.4.7-23)"; let java_11 = "OpenJDK 64-Bit Server VM (11.0.4+11-post-Ubuntu-1ubuntu219.04) for linux-amd64 JRE (11.0.4+11-post-Ubuntu-1ubuntu219.04), built on Jul 18 2019 18:21:46 by \"build\" with gcc 8.3.0"; assert_eq!( - parse_java_version("v${raw}", java_11), + format_java_version(java_11, "v${raw}"), Some("v11.0.4".to_string()) ); assert_eq!( - parse_java_version("v${raw}", java_8), + format_java_version(java_8, "v${raw}"), Some("v1.8.0".to_string()) ); } #[test] - fn test_parse_java_version_oracle() { + fn test_format_java_version_oracle() { let java_8 = "Java HotSpot(TM) Client VM (25.65-b01) for linux-arm-vfp-hflt JRE (1.8.0_65-b17), built on Oct 6 2015 16:19:04 by \"java_re\" with gcc 4.7.2 20120910 (prerelease)"; assert_eq!( - parse_java_version("v${raw}", java_8), + format_java_version(java_8, "v${raw}"), Some("v1.8.0".to_string()) ); } #[test] - fn test_parse_java_version_redhat() { + fn test_format_java_version_redhat() { let java_8 = "OpenJDK 64-Bit Server VM (25.222-b10) for linux-amd64 JRE (1.8.0_222-b10), built on Jul 11 2019 20:48:53 by \"root\" with gcc 7.3.1 20180303 (Red Hat 7.3.1-5)"; let java_12 = "OpenJDK 64-Bit Server VM (12.0.2+10) for linux-amd64 JRE (12.0.2+10), built on Jul 18 2019 14:41:47 by \"jenkins\" with gcc 7.3.1 20180303 (Red Hat 7.3.1-5)"; assert_eq!( - parse_java_version("v${raw}", java_8), + format_java_version(java_8, "v${raw}"), Some("v1.8.0".to_string()) ); assert_eq!( - parse_java_version("v${raw}", java_12), + format_java_version(java_12, "v${raw}"), Some("v12.0.2".to_string()) ); } #[test] - fn test_parse_java_version_zulu() { + fn test_format_java_version_zulu() { let java_8 = "OpenJDK 64-Bit Server VM (25.222-b10) for linux-amd64 JRE (Zulu 8.40.0.25-CA-linux64) (1.8.0_222-b10), built on Jul 11 2019 11:36:39 by \"zulu_re\" with gcc 4.4.7 20120313 (Red Hat 4.4.7-3)"; let java_11 = "OpenJDK 64-Bit Server VM (11.0.4+11-LTS) for linux-amd64 JRE (Zulu11.33+15-CA) (11.0.4+11-LTS), built on Jul 11 2019 21:37:17 by \"zulu_re\" with gcc 4.9.2 20150212 (Red Hat 4.9.2-6)"; assert_eq!( - parse_java_version("v${raw}", java_8), + format_java_version(java_8, "v${raw}"), Some("v1.8.0".to_string()) ); assert_eq!( - parse_java_version("v${raw}", java_11), + format_java_version(java_11, "v${raw}"), Some("v11.0.4".to_string()) ); } #[test] - fn test_parse_java_version_eclipse_openj9() { + fn test_format_java_version_eclipse_openj9() { let java_8 = "Eclipse OpenJ9 OpenJDK 64-bit Server VM (1.8.0_222-b10) from linux-amd64 JRE with Extensions for OpenJDK for Eclipse OpenJ9 8.0.222.0, built on Jul 17 2019 21:29:18 by jenkins with g++ (GCC) 7.3.1 20180303 (Red Hat 7.3.1-5)"; let java_11 = "Eclipse OpenJ9 OpenJDK 64-bit Server VM (11.0.4+11) from linux-amd64 JRE with Extensions for OpenJDK for Eclipse OpenJ9 11.0.4.0, built on Jul 17 2019 21:51:37 by jenkins with g++ (GCC) 7.3.1 20180303 (Red Hat 7.3.1-5)"; assert_eq!( - parse_java_version("v${raw}", java_8), + format_java_version(java_8, "v${raw}"), Some("v1.8.0".to_string()) ); assert_eq!( - parse_java_version("v${raw}", java_11), + format_java_version(java_11, "v${raw}"), Some("v11.0.4".to_string()) ); } #[test] - fn test_parse_java_version_graalvm() { + fn test_format_java_version_graalvm() { let java_8 = "OpenJDK 64-Bit GraalVM CE 19.2.0.1 (25.222-b08-jvmci-19.2-b02) for linux-amd64 JRE (8u222), built on Jul 19 2019 17:37:13 by \"buildslave\" with gcc 7.3.0"; assert_eq!( - parse_java_version("v${raw}", java_8), + format_java_version(java_8, "v${raw}"), Some("v8".to_string()) ); } #[test] - fn test_parse_java_version_amazon_corretto() { + fn test_format_java_version_amazon_corretto() { let java_8 = "OpenJDK 64-Bit Server VM (25.222-b10) for linux-amd64 JRE (1.8.0_222-b10), built on Jul 11 2019 20:48:53 by \"root\" with gcc 7.3.1 20180303 (Red Hat 7.3.1-5)"; let java_11 = "OpenJDK 64-Bit Server VM (11.0.4+11-LTS) for linux-amd64 JRE (11.0.4+11-LTS), built on Jul 11 2019 20:06:11 by \"\" with gcc 7.3.1 20180303 (Red Hat 7.3.1-5)"; assert_eq!( - parse_java_version("v${raw}", java_8), + format_java_version(java_8, "v${raw}"), Some("v1.8.0".to_string()) ); assert_eq!( - parse_java_version("v${raw}", java_11), + format_java_version(java_11, "v${raw}"), Some("v11.0.4".to_string()) ); } #[test] - fn test_parse_java_version_sapmachine() { + fn test_format_java_version_sapmachine() { let java_11 = "OpenJDK 64-Bit Server VM (11.0.4+11-LTS-sapmachine) for linux-amd64 JRE (11.0.4+11-LTS-sapmachine), built on Jul 17 2019 08:58:43 by \"\" with gcc 7.3.0"; assert_eq!( - parse_java_version("v${raw}", java_11), + format_java_version(java_11, "v${raw}"), Some("v11.0.4".to_string()) ); } #[test] - fn test_parse_java_version_unknown() { + fn test_format_java_version_unknown() { let unknown_jre = "Unknown JRE"; - assert_eq!(parse_java_version("v${raw}", unknown_jre), None); + assert_eq!(format_java_version(unknown_jre, "v${raw}"), None); } #[test] diff --git a/src/modules/nodejs.rs b/src/modules/nodejs.rs index b299344ab..1bcfbdda3 100644 --- a/src/modules/nodejs.rs +++ b/src/modules/nodejs.rs @@ -57,18 +57,10 @@ pub fn module<'a>(context: &'a Context) -> Option> { _ => None, }) .map(|variable| match variable { - "version" => { - let nodejs_detected_version = &nodejs_version - .deref() - .as_ref() - .map(|version| version.trim()); - match nodejs_detected_version { - Some(version) => Some(Ok(VersionFormatter::new(config.version_format) - .ok()? - .format_version(&version.to_string().drain(1..).collect::()))), - None => None, - } - } + "version" => Some(Ok(format_node_version( + nodejs_version.deref().as_ref()?, + config.version_format, + ))), _ => None, }) .parse(None) @@ -114,6 +106,18 @@ fn check_engines_version(nodejs_version: &str, engines_version: Option) r.matches(&v) } +fn format_node_version(node_version: &str, version_format: &str) -> String { + let version = node_version.trim_start_matches('v').trim(); + + match VersionFormatter::format_version(version, version_format) { + Ok(formatted) => formatted, + Err(error) => { + log::warn!("Error formating `node` version:\n{}", error); + format!("v{}", version) + } + } +} + #[cfg(test)] mod tests { use crate::test::ModuleRenderer; diff --git a/src/modules/python.rs b/src/modules/python.rs index 98a6e5ab0..ace3e965c 100644 --- a/src/modules/python.rs +++ b/src/modules/python.rs @@ -41,10 +41,7 @@ pub fn module<'a>(context: &'a Context) -> Option> { _ => None, }) .map(|variable| match variable { - "version" => { - let version = get_python_version(context, &config)?; - Some(Ok(version.trim().to_string())) - } + "version" => get_python_version(context, &config).map(Ok), "virtualenv" => { let virtual_env = get_python_virtual_env(context); virtual_env.as_ref().map(|e| Ok(e.trim().to_string())) @@ -68,7 +65,8 @@ pub fn module<'a>(context: &'a Context) -> Option> { fn get_python_version(context: &Context, config: &PythonConfig) -> Option { if config.pyenv_version_name { - return Some(context.exec_cmd("pyenv", &["version-name"])?.stdout); + let version_name = context.exec_cmd("pyenv", &["version-name"])?.stdout; + return Some(version_name.trim().to_string()); }; let version = config .python_binary @@ -83,21 +81,23 @@ fn get_python_version(context: &Context, config: &PythonConfig) -> Option Option { +fn format_python_version(python_version: &str, version_format: &str) -> Option { let version = python_version // split into ["Python", "3.8.6", ...] .split_whitespace() // get down to "3.8.6" .nth(1)?; - Some( - VersionFormatter::new(version_format) - .ok()? - .format_version(version), - ) + match VersionFormatter::format_version(version, version_format) { + Ok(formatted) => Some(formatted), + Err(error) => { + log::warn!("Error formating `python` version:\n{}", error); + Some(format!("v{}", version)) + } + } } fn get_python_virtual_env(context: &Context) -> Option { @@ -128,45 +128,48 @@ mod tests { #[test] fn test_format_python_version() { - let input = "Python 3.7.2"; assert_eq!( - format_python_version("v${major}.${minor}.${patch}", input), + format_python_version("Python 3.7.2", "v${major}.${minor}.${patch}"), Some("v3.7.2".to_string()) ); } #[test] fn test_format_python_version_truncated() { - let input = "Python 3.7.2"; assert_eq!( - format_python_version("v${major}.${minor}", input), + format_python_version("Python 3.7.2", "v${major}.${minor}"), Some("v3.7".to_string()) ); } #[test] fn test_format_python_version_is_malformed() { - let input = "Python 3.7"; assert_eq!( - format_python_version("v${major}.${minor}.${patch}", input), + format_python_version("Python 3.7", "v${major}.${minor}.${patch}"), Some("v3.7.".to_string()) ); } #[test] fn test_format_python_version_anaconda() { - let input = "Python 3.6.10 :: Anaconda, Inc."; assert_eq!( - format_python_version("v${major}.${minor}.${patch}", input), + format_python_version( + "Python 3.6.10 :: Anaconda, Inc.", + "v${major}.${minor}.${patch}" + ), Some("v3.6.10".to_string()) ); } #[test] fn test_format_python_version_pypy() { - let input = "Python 3.7.9 (7e6e2bb30ac5fbdbd443619cae28c51d5c162a02, Nov 24 2020, 10:03:59)\n[PyPy 7.3.3-beta0 with GCC 10.2.0]"; assert_eq!( - format_python_version("v${major}.${minor}.${patch}", input), + format_python_version( + "\ +Python 3.7.9 (7e6e2bb30ac5fbdbd443619cae28c51d5c162a02, Nov 24 2020, 10:03:59) +[PyPy 7.3.3-beta0 with GCC 10.2.0]", + "v${major}.${minor}.${patch}" + ), Some("v3.7.9".to_string()) ); } diff --git a/src/modules/ruby.rs b/src/modules/ruby.rs index 0842945f6..26b6a3f3d 100644 --- a/src/modules/ruby.rs +++ b/src/modules/ruby.rs @@ -35,8 +35,8 @@ pub fn module<'a>(context: &'a Context) -> Option> { }) .map(|variable| match variable { "version" => format_ruby_version( - &config, - &context.exec_cmd("ruby", &["-v"])?.stdout.as_str(), + &context.exec_cmd("ruby", &["-v"])?.stdout, + config.version_format, ) .map(Ok), _ => None, @@ -55,7 +55,7 @@ pub fn module<'a>(context: &'a Context) -> Option> { Some(module) } -fn format_ruby_version(config: &RubyConfig, ruby_version: &str) -> Option { +fn format_ruby_version(ruby_version: &str, version_format: &str) -> Option { let version = ruby_version // split into ["ruby", "2.6.0p0", "linux/amd64"] .split_whitespace() @@ -66,11 +66,13 @@ fn format_ruby_version(config: &RubyConfig, ruby_version: &str) -> Option Some(formatted), + Err(error) => { + log::warn!("Error formating `ruby` version:\n{}", error); + Some(format!("v{}", version)) + } + } } #[cfg(test)] @@ -130,25 +132,25 @@ mod tests { #[test] fn test_format_ruby_version() { - let test_config = RubyConfig::default(); + let config = RubyConfig::default(); assert_eq!( format_ruby_version( - &test_config, - "ruby 2.1.10p492 (2016-04-01 revision 54464) [x86_64-darwin19.0]" + "ruby 2.1.10p492 (2016-04-01 revision 54464) [x86_64-darwin19.0]", + config.version_format ), Some("v2.1.10".to_string()) ); assert_eq!( format_ruby_version( - &test_config, - "ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux-gnu]" + "ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux-gnu]", + config.version_format ), Some("v2.5.1".to_string()) ); assert_eq!( format_ruby_version( - &test_config, - "ruby 2.7.0p0 (2019-12-25 revision 647ee6f091) [x86_64-linux-musl]" + "ruby 2.7.0p0 (2019-12-25 revision 647ee6f091) [x86_64-linux-musl]", + config.version_format ), Some("v2.7.0".to_string()) ); diff --git a/src/modules/rust.rs b/src/modules/rust.rs index 963522a9c..a465d9798 100644 --- a/src/modules/rust.rs +++ b/src/modules/rust.rs @@ -72,27 +72,25 @@ fn get_module_version(context: &Context, config: &RustConfig) -> Option // - `rustup show` // - `rustup show active-toolchain` // - `rustup which` - let module_version = if let Some(toolchain) = env_rustup_toolchain(context) + if let Some(toolchain) = env_rustup_toolchain(context) .or_else(|| execute_rustup_override_list(&context.current_dir)) .or_else(|| find_rust_toolchain_file(&context)) { match execute_rustup_run_rustc_version(&toolchain) { - RustupRunRustcVersionOutcome::RustcVersion(stdout) => { - format_rustc_version(config, stdout) + RustupRunRustcVersionOutcome::RustcVersion(rustc_version) => { + format_rustc_version(&rustc_version, config.version_format) } - RustupRunRustcVersionOutcome::ToolchainName(toolchain) => toolchain, + RustupRunRustcVersionOutcome::ToolchainName(toolchain) => Some(toolchain), RustupRunRustcVersionOutcome::RustupNotWorking => { // If `rustup` is not in `$PATH` or cannot be executed for other reasons, we can // safely execute `rustc --version`. - format_rustc_version(config, execute_rustc_version()?) + format_rustc_version(&execute_rustc_version()?, config.version_format) } - RustupRunRustcVersionOutcome::Err => return None, + RustupRunRustcVersionOutcome::Err => None, } } else { - format_rustc_version(config, execute_rustc_version()?) - }; - - Some(module_version) + format_rustc_version(&execute_rustc_version()?, config.version_format) + } } fn env_rustup_toolchain(context: &Context) -> Option { @@ -205,14 +203,19 @@ fn execute_rustc_version() -> Option { } } -fn format_rustc_version(config: &RustConfig, mut rustc_stdout: String) -> String { - let offset = &rustc_stdout.find('(').unwrap_or_else(|| rustc_stdout.len()); - let formatted_version: String = rustc_stdout.drain(..offset).collect(); - let full_version_string = formatted_version.replace("rustc", ""); - let version_string = full_version_string.trim(); - match VersionFormatter::new(config.version_format) { - Ok(formatter) => formatter.format_version(&version_string), - _ => format!("v{}", version_string), +fn format_rustc_version(rustc_version: &str, version_format: &str) -> Option { + let version = rustc_version + // split into ["rustc", "1.34.0", ...] + .split_whitespace() + // get down to "1.34.0" + .nth(1)?; + + match VersionFormatter::format_version(version, version_format) { + Ok(formatted) => Some(formatted), + Err(error) => { + log::warn!("Error formating `rust` version:\n{}", error); + Some(format!("v{}", version)) + } } } @@ -326,27 +329,25 @@ mod tests { #[test] fn test_format_rustc_version() { - let test_config: RustConfig = RustConfig::default(); - - let nightly_input = String::from("rustc 1.34.0-nightly (b139669f3 2019-04-10)"); + let config = RustConfig::default(); + let rustc_stable = "rustc 1.34.0 (91856ed52 2019-04-10)"; + let rustc_beta = "rustc 1.34.0-beta.1 (2bc1d406d 2019-04-10)"; + let rustc_nightly = "rustc 1.34.0-nightly (b139669f3 2019-04-10)"; assert_eq!( - format_rustc_version(&test_config, nightly_input), - "v1.34.0-nightly" + format_rustc_version(rustc_nightly, config.version_format), + Some("v1.34.0-nightly".to_string()) ); - - let beta_input = String::from("rustc 1.34.0-beta.1 (2bc1d406d 2019-04-10)"); assert_eq!( - format_rustc_version(&test_config, beta_input), - "v1.34.0-beta.1" + format_rustc_version(rustc_beta, config.version_format), + Some("v1.34.0-beta.1".to_string()) ); - - let stable_input = String::from("rustc 1.34.0 (91856ed52 2019-04-10)"); - assert_eq!(format_rustc_version(&test_config, stable_input), "v1.34.0"); - - let version_without_hash = String::from("rustc 1.34.0"); assert_eq!( - format_rustc_version(&test_config, version_without_hash), - "v1.34.0" + format_rustc_version(rustc_stable, config.version_format), + Some("v1.34.0".to_string()) + ); + assert_eq!( + format_rustc_version("rustc 1.34.0", config.version_format), + Some("v1.34.0".to_string()) ); }