Skip to content

Commit e4b4114

Browse files
authored
Make extract_requirement_version return an Option (#294)
Rust 1.83 added support for `Option::expect` in `const` contexts: https://blog.rust-lang.org/2024/11/28/Rust-1.83.0.html#stabilized-apis This means we can now return an `Option` from `extract_requirement_version` instead of panicing within it, which makes it easier to test the function, and avoids the need to explain that the panic inside the function was actually safe.
1 parent 148ccb9 commit e4b4114

File tree

2 files changed

+17
-12
lines changed

2 files changed

+17
-12
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
name = "python-buildpack"
33
edition = "2021"
4-
rust-version = "1.80"
4+
rust-version = "1.83"
55
# Disable automatic integration test discovery, since we import them in main.rs (see comment there).
66
autotests = false
77

src/packaging_tool_versions.rs

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,28 @@ use std::str;
44
// Each file must contain a single package specifier in the format `package==1.2.3`,
55
// from which we extract/validate the version substring at compile time.
66
pub(crate) const PIP_VERSION: &str =
7-
extract_requirement_version(include_str!("../requirements/pip.txt"));
7+
extract_requirement_version(include_str!("../requirements/pip.txt"))
8+
.expect("pip.txt must contain 'pip==VERSION'");
89
pub(crate) const POETRY_VERSION: &str =
9-
extract_requirement_version(include_str!("../requirements/poetry.txt"));
10+
extract_requirement_version(include_str!("../requirements/poetry.txt"))
11+
.expect("poetry.txt must contain 'poetry==VERSION'");
1012

1113
// Extract the version substring from an exact-version package specifier (such as `foo==1.2.3`).
1214
// This function should only be used to extract the version constants from the buildpack's own
1315
// requirements files, which are controlled by us and don't require a full PEP 508 version parser.
14-
// Since this is a `const fn` we cannot use iterators, most methods on `str`, `Result::expect` etc.
15-
const fn extract_requirement_version(requirement: &'static str) -> &'static str {
16+
// Note: Since this is a `const fn` we cannot use iterators and most methods on `str` / `Result`.
17+
const fn extract_requirement_version(requirement: &'static str) -> Option<&'static str> {
1618
let mut bytes = requirement.as_bytes();
1719
while let [_, rest @ ..] = bytes {
1820
if let [b'=', b'=', version @ ..] = rest {
1921
if let Ok(version) = str::from_utf8(version.trim_ascii()) {
20-
return version;
22+
return Some(version);
2123
}
2224
break;
2325
}
2426
bytes = rest;
2527
}
26-
// This is safe, since this function is only used at compile time.
27-
panic!("Requirement must be in the format: 'package==X.Y.Z'");
28+
None
2829
}
2930

3031
#[cfg(test)]
@@ -33,13 +34,17 @@ mod tests {
3334

3435
#[test]
3536
fn extract_requirement_version_valid() {
36-
assert_eq!(extract_requirement_version("package==1.2.3"), "1.2.3");
37-
assert_eq!(extract_requirement_version("\npackage == 0.12\n"), "0.12");
37+
assert_eq!(extract_requirement_version("package==1.2.3"), Some("1.2.3"));
38+
assert_eq!(
39+
extract_requirement_version("\npackage == 0.12\n"),
40+
Some("0.12")
41+
);
3842
}
3943

4044
#[test]
41-
#[should_panic(expected = "Requirement must be in the format")]
4245
fn extract_requirement_version_invalid() {
43-
extract_requirement_version("package=<1.2.3");
46+
assert_eq!(extract_requirement_version(""), None);
47+
assert_eq!(extract_requirement_version("package"), None);
48+
assert_eq!(extract_requirement_version("package=<1.2.3"), None);
4449
}
4550
}

0 commit comments

Comments
 (0)