#![cfg_attr(not(feature = "api-custom"), allow(unused_variables, dead_code))]
use std::error::Error;
use std::str::FromStr;
use regex::{Captures, Regex};
use crate::GodotVersion;
pub fn parse_godot_version(version_str: &str) -> Result<GodotVersion, Box<dyn Error>> {
let regex = Regex::new(
r"(?xm)
# x: ignore whitespace and allow line comments (starting with `#`)
# m: multi-line mode, ^ and $ match start and end of line
^
(?P<major>\d+)
\.(?P<minor>\d+)
# Patch version is omitted if it's zero.
(?:\.(?P<patch>\d+))?
# stable|dev|alpha|beta|rc12|... Can be set through an env var when the engine is built.
\.(?P<status>[^.]+)
# Capture both module config and build, could be multiple components:
# mono|official|custom_build|gentoo|arch_linux|...
# Notice +? for non-greedy match.
(\.[^.]+)+?
# Git commit SHA1, currently truncated to 9 chars, but accept the full thing
(?:\.(?P<custom_rev>[a-f0-9]{9,40}))?
# Optional newline printed in some systems (e.g. Arch Linux, see #416)
(?:\\n)?
$
",
)?;
let fail = || format!("Version substring cannot be parsed: `{version_str}`");
let caps = regex.captures(version_str).ok_or_else(fail)?;
Ok(GodotVersion {
full_string: caps.get(0).unwrap().as_str().trim().to_string(),
major: cap(&caps, "major")?.unwrap(),
minor: cap(&caps, "minor")?.unwrap(),
patch: cap(&caps, "patch")?.unwrap_or(0),
status: cap(&caps, "status")?.unwrap(),
custom_rev: cap(&caps, "custom_rev")?,
})
}
pub(crate) fn validate_godot_version(godot_version: &GodotVersion) {
assert_eq!(
godot_version.major, 4,
"Only Godot versions with major version 4 are supported; found version {}.",
godot_version.full_string
);
assert!(
godot_version.minor > 0,
"Godot 4.0 is no longer supported by godot-rust; found version {}.",
godot_version.full_string
);
}
fn cap<T: FromStr>(caps: &Captures, key: &str) -> Result<Option<T>, Box<dyn Error>> {
caps.name(key)
.map(|m| m.as_str().parse())
.transpose()
.map_err(|_| {
format!(
"Version string cannot be parsed: `{}`",
caps.get(0).unwrap().as_str()
)
.into()
})
}
#[test]
#[rustfmt::skip]
fn test_godot_versions() {
fn s(s: &str) -> Option<String> {
Some(s.to_string())
}
let good_versions = [
("3.0.stable.official", 3, 0, 0, "stable", None),
("3.0.1.stable.official", 3, 0, 1, "stable", None),
("3.2.stable.official", 3, 2, 0, "stable", None),
("3.37.stable.official", 3, 37, 0, "stable", None),
("3.4.stable.official.206ba70f4", 3, 4, 0, "stable", s("206ba70f4")),
("3.4.1.stable.official.aa1b95889", 3, 4, 1, "stable", s("aa1b95889")),
("3.5.beta.custom_build.837f2c5f8", 3, 5, 0, "beta", s("837f2c5f8")),
("4.0.beta8.gentoo.45cac42c0", 4, 0, 0, "beta8", s("45cac42c0")),
("4.0.dev.custom_build.e7e9e663b", 4, 0, 0, "dev", s("e7e9e663b")),
("4.0.alpha.custom_build.faddbcfc0", 4, 0, 0, "alpha", s("faddbcfc0")),
("4.0.beta8.mono.custom_build.b28ddd918", 4, 0, 0, "beta8", s("b28ddd918")),
("4.0.rc1.official.8843d9ad3", 4, 0, 0, "rc1", s("8843d9ad3")),
("4.0.stable.arch_linux", 4, 0, 0, "stable", None),
("4.1.1.stable.arch_linux\n", 4, 1, 1, "stable", None),
("arguments
0: /Users/runner/work/_temp/godot_bin/godot.macos.editor.dev.x86_64
1: --version
Current path: /Users/runner/work/gdext/gdext/godot-core
4.1.dev.custom_build.79454bfd3", 4, 1, 0, "dev", s("79454bfd3")),
];
let bad_versions = [
"Godot Engine v4.0.stable.arch_linux - https://godotengine.org", "3.stable.official.206ba70f4", "4.0.stable", ];
for (full, major, minor, patch, status, custom_rev) in good_versions {
let expected = GodotVersion {
full_string: full.lines().last().unwrap().trim().to_owned(),
major,
minor,
patch,
status: status.to_owned(),
custom_rev,
};
let parsed: GodotVersion = parse_godot_version(full).unwrap();
assert_eq!(parsed, expected, "{full}");
}
for full in bad_versions {
let parsed = parse_godot_version(full);
assert!(parsed.is_err(), "{}", full);
}
}