diff --git a/crates/rb-sys-env/src/ruby_version.rs b/crates/rb-sys-env/src/ruby_version.rs index 43a330ea4..a4383eb46 100644 --- a/crates/rb-sys-env/src/ruby_version.rs +++ b/crates/rb-sys-env/src/ruby_version.rs @@ -128,7 +128,7 @@ impl RubyVersion { let mut ruby_version = env_ruby_version .split('.') - .map(|s| s.parse().expect("version component is not a number")); + .map(|s| parse_version_component(s, "RUBY_VERSION")); Self { major: ruby_version.next().expect("major"), @@ -140,9 +140,20 @@ impl RubyVersion { } } +fn parse_version_component(value: &str, name: &str) -> u8 { + let digits: String = value.chars().take_while(|c| c.is_ascii_digit()).collect(); + if digits.is_empty() { + panic!("{} is not a number", name); + } + digits.parse().expect("version component is not a number") +} + #[cfg(test)] mod tests { use super::*; + use std::sync::Mutex; + + static ENV_LOCK: Mutex<()> = Mutex::new(()); #[test] fn test_equality_from_tuple() { @@ -162,4 +173,46 @@ mod tests { RubyVersion::from((3, 0, 0)) ); } + + #[test] + fn test_from_hashmap_with_ruby_version_suffix() { + let mut env = HashMap::new(); + env.insert("ruby_version".to_string(), "4.1.0+1".to_string()); + + assert_eq!( + RubyVersion::from_raw_environment(&env), + RubyVersion::from((4, 1, 0)) + ); + } + + #[test] + fn test_from_env_with_ruby_version_suffix() { + let _guard = ENV_LOCK.lock().unwrap(); + let previous = std::env::var("RUBY_VERSION").ok(); + + std::env::set_var("RUBY_VERSION", "4.1.0+1"); + let env = HashMap::new(); + + let parsed = RubyVersion::from_raw_environment(&env); + + if let Some(value) = previous { + std::env::set_var("RUBY_VERSION", value); + } else { + std::env::remove_var("RUBY_VERSION"); + } + + assert_eq!(parsed, RubyVersion::from((4, 1, 0))); + } + + #[test] + fn test_from_hashmap_rejects_major_suffix() { + let mut env = HashMap::new(); + env.insert("MAJOR".to_string(), "4+1".to_string()); + env.insert("MINOR".to_string(), "1".to_string()); + env.insert("TEENY".to_string(), "0".to_string()); + + let result = std::panic::catch_unwind(|| RubyVersion::from_raw_environment(&env)); + + assert!(result.is_err()); + } } diff --git a/crates/rb-sys/build/version.rs b/crates/rb-sys/build/version.rs index af35813e9..b052dba7e 100644 --- a/crates/rb-sys/build/version.rs +++ b/crates/rb-sys/build/version.rs @@ -43,3 +43,16 @@ impl std::fmt::Display for Version { write!(f, "{}.{}", self.0, self.1) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_current_with_ruby_version_suffix() { + let mut rbconfig = RbConfig::default(); + rbconfig.set_value_for_key("ruby_version", "4.1.0+1".to_string()); + + assert_eq!(Version::current(&rbconfig), Version::new(4, 1)); + } +} diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index a2977af30..af479528d 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -195,14 +195,14 @@ dependencies = [ [[package]] name = "rb-sys" -version = "0.9.114" +version = "0.9.124" dependencies = [ "rb-sys-build", ] [[package]] name = "rb-sys-build" -version = "0.9.114" +version = "0.9.124" dependencies = [ "bindgen", "lazy_static", @@ -215,7 +215,7 @@ dependencies = [ [[package]] name = "rb-sys-env" -version = "0.2.2" +version = "0.2.3" [[package]] name = "rb-sys-fuzz" @@ -228,7 +228,7 @@ dependencies = [ [[package]] name = "rb-sys-test-helpers" -version = "0.2.2" +version = "0.3.0" dependencies = [ "rb-sys", "rb-sys-env", @@ -237,7 +237,7 @@ dependencies = [ [[package]] name = "rb-sys-test-helpers-macros" -version = "0.2.2" +version = "0.3.0" dependencies = [ "quote", "syn",