#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RubyVersion {
pub engine: String,
pub version: String,
}
impl RubyVersion {
pub fn parse(input: &str) -> Option<Self> {
let trimmed = input.trim();
let (engine, version_raw) = trimmed.split_once(' ')?;
let version = strip_patchlevel(version_raw);
Some(RubyVersion {
engine: engine.to_string(),
version,
})
}
}
fn strip_patchlevel(version: &str) -> String {
if let Some(pos) = version.rfind('p') {
let before = &version[..pos];
let after = &version[pos + 1..];
if !after.is_empty()
&& after.chars().all(|c| c.is_ascii_digit())
&& before.ends_with(|c: char| c.is_ascii_digit())
{
return before.to_string();
}
}
version.to_string()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_ruby_with_patchlevel() {
let rv = RubyVersion::parse("ruby 3.0.0p0").unwrap();
assert_eq!(rv.engine, "ruby");
assert_eq!(rv.version, "3.0.0");
}
#[test]
fn parse_ruby_with_high_patchlevel() {
let rv = RubyVersion::parse("ruby 2.7.6p219").unwrap();
assert_eq!(rv.engine, "ruby");
assert_eq!(rv.version, "2.7.6");
}
#[test]
fn parse_ruby_without_patchlevel() {
let rv = RubyVersion::parse("ruby 3.2.1").unwrap();
assert_eq!(rv.engine, "ruby");
assert_eq!(rv.version, "3.2.1");
}
#[test]
fn parse_jruby() {
let rv = RubyVersion::parse("jruby 9.3.6.0").unwrap();
assert_eq!(rv.engine, "jruby");
assert_eq!(rv.version, "9.3.6.0");
}
#[test]
fn parse_mruby() {
let rv = RubyVersion::parse("mruby 3.1.0").unwrap();
assert_eq!(rv.engine, "mruby");
assert_eq!(rv.version, "3.1.0");
}
#[test]
fn parse_with_leading_whitespace() {
let rv = RubyVersion::parse(" ruby 3.0.0p0 ").unwrap();
assert_eq!(rv.engine, "ruby");
assert_eq!(rv.version, "3.0.0");
}
#[test]
fn parse_empty_string() {
assert!(RubyVersion::parse("").is_none());
}
#[test]
fn parse_no_space() {
assert!(RubyVersion::parse("ruby").is_none());
}
#[test]
fn parse_empty_version() {
assert!(RubyVersion::parse("ruby ").is_none());
}
#[test]
fn strip_patchlevel_with_p0() {
assert_eq!(strip_patchlevel("3.0.0p0"), "3.0.0");
}
#[test]
fn strip_patchlevel_with_p219() {
assert_eq!(strip_patchlevel("2.7.6p219"), "2.7.6");
}
#[test]
fn strip_patchlevel_no_patchlevel() {
assert_eq!(strip_patchlevel("3.2.1"), "3.2.1");
}
#[test]
fn strip_patchlevel_preserves_pre_release() {
assert_eq!(strip_patchlevel("1.0.0.pre1"), "1.0.0.pre1");
}
}