gem_audit/lockfile/
ruby_version.rs1#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct RubyVersion {
9 pub engine: String,
11 pub version: String,
13}
14
15impl RubyVersion {
16 pub fn parse(input: &str) -> Option<Self> {
22 let trimmed = input.trim();
23 let (engine, version_raw) = trimmed.split_once(' ')?;
24 let version = strip_patchlevel(version_raw);
25
26 Some(RubyVersion {
27 engine: engine.to_string(),
28 version,
29 })
30 }
31}
32
33fn strip_patchlevel(version: &str) -> String {
39 if let Some(pos) = version.rfind('p') {
41 let before = &version[..pos];
42 let after = &version[pos + 1..];
43 if !after.is_empty()
46 && after.chars().all(|c| c.is_ascii_digit())
47 && before.ends_with(|c: char| c.is_ascii_digit())
48 {
49 return before.to_string();
50 }
51 }
52 version.to_string()
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58
59 #[test]
60 fn parse_ruby_with_patchlevel() {
61 let rv = RubyVersion::parse("ruby 3.0.0p0").unwrap();
62 assert_eq!(rv.engine, "ruby");
63 assert_eq!(rv.version, "3.0.0");
64 }
65
66 #[test]
67 fn parse_ruby_with_high_patchlevel() {
68 let rv = RubyVersion::parse("ruby 2.7.6p219").unwrap();
69 assert_eq!(rv.engine, "ruby");
70 assert_eq!(rv.version, "2.7.6");
71 }
72
73 #[test]
74 fn parse_ruby_without_patchlevel() {
75 let rv = RubyVersion::parse("ruby 3.2.1").unwrap();
76 assert_eq!(rv.engine, "ruby");
77 assert_eq!(rv.version, "3.2.1");
78 }
79
80 #[test]
81 fn parse_jruby() {
82 let rv = RubyVersion::parse("jruby 9.3.6.0").unwrap();
83 assert_eq!(rv.engine, "jruby");
84 assert_eq!(rv.version, "9.3.6.0");
85 }
86
87 #[test]
88 fn parse_mruby() {
89 let rv = RubyVersion::parse("mruby 3.1.0").unwrap();
90 assert_eq!(rv.engine, "mruby");
91 assert_eq!(rv.version, "3.1.0");
92 }
93
94 #[test]
95 fn parse_with_leading_whitespace() {
96 let rv = RubyVersion::parse(" ruby 3.0.0p0 ").unwrap();
97 assert_eq!(rv.engine, "ruby");
98 assert_eq!(rv.version, "3.0.0");
99 }
100
101 #[test]
102 fn parse_empty_string() {
103 assert!(RubyVersion::parse("").is_none());
104 }
105
106 #[test]
107 fn parse_no_space() {
108 assert!(RubyVersion::parse("ruby").is_none());
109 }
110
111 #[test]
112 fn parse_empty_version() {
113 assert!(RubyVersion::parse("ruby ").is_none());
114 }
115
116 #[test]
117 fn strip_patchlevel_with_p0() {
118 assert_eq!(strip_patchlevel("3.0.0p0"), "3.0.0");
119 }
120
121 #[test]
122 fn strip_patchlevel_with_p219() {
123 assert_eq!(strip_patchlevel("2.7.6p219"), "2.7.6");
124 }
125
126 #[test]
127 fn strip_patchlevel_no_patchlevel() {
128 assert_eq!(strip_patchlevel("3.2.1"), "3.2.1");
129 }
130
131 #[test]
132 fn strip_patchlevel_preserves_pre_release() {
133 assert_eq!(strip_patchlevel("1.0.0.pre1"), "1.0.0.pre1");
135 }
136}