Skip to content

Commit f997699

Browse files
committed
Allow version regex to match 4.0.stable.arch_linux
1 parent 54cb201 commit f997699

File tree

1 file changed

+61
-32
lines changed

1 file changed

+61
-32
lines changed

godot-codegen/src/godot_version.rs

Lines changed: 61 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66

77
//#![allow(unused_variables, dead_code)]
88

9-
use regex::Regex;
9+
use regex::{Captures, Regex};
1010
use std::error::Error;
11+
use std::str::FromStr;
1112

13+
#[derive(Debug, Eq, PartialEq)]
1214
pub struct GodotVersion {
1315
/// the original string (trimmed, stripped of text around)
1416
pub full_string: String,
@@ -20,40 +22,64 @@ pub struct GodotVersion {
2022
pub patch: u8,
2123

2224
/// alpha|beta|dev|stable
23-
pub stability: String,
25+
pub status: String,
2426

2527
/// Git revision 'custom_build.{rev}' or '{official}.rev', if available
2628
pub custom_rev: Option<String>,
2729
}
2830

2931
pub fn parse_godot_version(version_str: &str) -> Result<GodotVersion, Box<dyn Error>> {
32+
// Format of the string emitted by `godot --version`:
33+
// https://github.com/godot-rust/gdext/issues/118#issuecomment-1465748123
3034
let regex = Regex::new(
31-
// major minor [patch] official|custom_build|gentoo
32-
// v v v v
33-
r#"(\d+)\.(\d+)(?:\.(\d+))?\.(alpha|beta|dev|rc|stable)[0-9]*\.(?:mono\.)?(?:[a-z_]+\.([a-f0-9]+)|official)"#,
35+
r#"(?x) # ignore whitespace and allow line comments (starting with `#`)
36+
^
37+
(?P<major>\d+)
38+
\.(?P<minor>\d+)
39+
# Patch version is omitted if it's zero.
40+
(?:\.(?P<patch>\d+))?
41+
# stable|dev|alpha|beta|rc12|... Can be set through an env var when the engine is built.
42+
\.(?P<status>[^.]+)
43+
# Capture both module config and build, could be multiple components:
44+
# mono|official|custom_build|gentoo|arch_linux|...
45+
# Notice +? for non-greedy match.
46+
(\.[^.]+)+?
47+
# Git commit SHA1, currently truncated to 9 chars, but accept the full thing
48+
(?:\.(?P<custom_rev>[a-f0-9]{9,40}))?
49+
$
50+
"#,
3451
)?;
3552

3653
let fail = || format!("Version substring cannot be parsed: `{version_str}`");
3754
let caps = regex.captures(version_str).ok_or_else(fail)?;
3855

3956
Ok(GodotVersion {
4057
full_string: caps.get(0).unwrap().as_str().to_string(),
41-
major: caps.get(1).ok_or_else(fail)?.as_str().parse::<u8>()?,
42-
minor: caps.get(2).ok_or_else(fail)?.as_str().parse::<u8>()?,
43-
patch: caps
44-
.get(3)
45-
.map(|m| m.as_str().parse::<u8>())
46-
.transpose()?
47-
.unwrap_or(0),
48-
stability: caps.get(4).ok_or_else(fail)?.as_str().to_string(),
49-
custom_rev: caps.get(5).map(|m| m.as_str().to_string()),
58+
major: cap(&caps, "major")?.unwrap(),
59+
minor: cap(&caps, "minor")?.unwrap(),
60+
patch: cap(&caps, "patch")?.unwrap_or(0),
61+
status: cap(&caps, "status")?.unwrap(),
62+
custom_rev: cap(&caps, "custom_rev")?,
5063
})
5164
}
5265

66+
/// Extracts and parses a named capture group from a regex match.
67+
fn cap<T: FromStr>(caps: &Captures, key: &str) -> Result<Option<T>, Box<dyn Error>> {
68+
caps.name(key)
69+
.map(|m| m.as_str().parse())
70+
.transpose()
71+
.map_err(|_| {
72+
format!(
73+
"Version string cannot be parsed: `{}`",
74+
caps.get(0).unwrap().as_str()
75+
)
76+
.into()
77+
})
78+
}
79+
5380
// ----------------------------------------------------------------------------------------------------------------------------------------------
5481

5582
#[test]
56-
#[rustfmt::skip]
5783
fn test_godot_versions() {
5884
fn s(s: &str) -> Option<String> {
5985
Some(s.to_string())
@@ -64,35 +90,38 @@ fn test_godot_versions() {
6490
("3.0.1.stable.official", 3, 0, 1, "stable", None),
6591
("3.2.stable.official", 3, 2, 0, "stable", None),
6692
("3.37.stable.official", 3, 37, 0, "stable", None),
67-
("3.4.stable.official.206ba70f4", 3, 4, 0, "stable", s("206ba70f4")),
68-
("3.4.1.stable.official.aa1b95889", 3, 4, 1, "stable", s("aa1b95889")),
93+
("3.4.stable.official.206ba70f4", 3, 4, 0, "stable", s("206ba70f4")),
94+
("3.4.1.stable.official.aa1b95889", 3, 4, 1, "stable", s("aa1b95889")),
6995
("3.5.beta.custom_build.837f2c5f8", 3, 5, 0, "beta", s("837f2c5f8")),
70-
("4.0.beta8.gentoo.45cac42c0", 4, 0, 0, "beta", s("45cac42c0")),
96+
("4.0.beta8.gentoo.45cac42c0", 4, 0, 0, "beta8", s("45cac42c0")),
7197
("4.0.dev.custom_build.e7e9e663b", 4, 0, 0, "dev", s("e7e9e663b")),
7298
("4.0.alpha.custom_build.faddbcfc0", 4, 0, 0, "alpha", s("faddbcfc0")),
73-
("4.0.beta8.mono.custom_build.b28ddd918", 4, 0, 0, "beta", s("b28ddd918")),
74-
("4.0.rc1.official.8843d9ad3", 4, 0, 0, "rc", s("8843d9ad3")),
99+
("4.0.beta8.mono.custom_build.b28ddd918", 4, 0, 0, "beta8", s("b28ddd918")),
100+
("4.0.rc1.official.8843d9ad3", 4, 0, 0, "rc1", s("8843d9ad3")),
101+
("4.0.stable.arch_linux", 4, 0, 0, "stable", None),
75102
];
76103

77104
let bad_versions = [
78-
"4.0.unstable.custom_build.e7e9e663b", // 'unstable'
79-
"4.0.3.custom_build.e7e9e663b", // no stability
80-
"3.stable.official.206ba70f4", // no minor
81-
"4.0.alpha.custom_build", // no rev after 'custom_build' (this is allowed for 'official' however)
105+
"Godot Engine v4.0.stable.arch_linux - https://godotengine.org", // Surrounding cruft
106+
"3.stable.official.206ba70f4", // No minor version
107+
"4.0.stable", // No build type
82108
];
83109

84-
for (full, major, minor, patch, stability, custom_rev) in good_versions {
110+
for (full, major, minor, patch, status, custom_rev) in good_versions {
111+
let expected = GodotVersion {
112+
full_string: full.to_owned(),
113+
major,
114+
minor,
115+
patch,
116+
status: status.to_owned(),
117+
custom_rev,
118+
};
85119
let parsed: GodotVersion = parse_godot_version(full).unwrap();
86-
assert_eq!(parsed.major, major);
87-
assert_eq!(parsed.minor, minor);
88-
assert_eq!(parsed.patch, patch);
89-
assert_eq!(parsed.stability, stability);
90-
assert_eq!(parsed.custom_rev, custom_rev);
91-
assert_eq!(parsed.full_string, full);
120+
assert_eq!(parsed, expected, "{}", full);
92121
}
93122

94123
for full in bad_versions {
95124
let parsed = parse_godot_version(full);
96-
assert!(parsed.is_err());
125+
assert!(parsed.is_err(), "{}", full);
97126
}
98127
}

0 commit comments

Comments
 (0)