//! Nix version parsing and conditional compilation support. /// Emit [`cargo:rustc-cfg`] directives for Nix version-based conditional compilation. /// /// Call from build.rs with the Nix version and desired version gates. /// /// [`cargo:rustc-cfg`]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-cfg /// /// # Example /// /// ``` /// use nix_bindings_util::nix_version::emit_version_cfg; /// # // Stub pkg_config so that we can render a full usage example /// # mod pkg_config { pub fn probe_library(_: &str) -> Result { Ok(Library { version: "2.33.0pre".into() }) } /// # pub struct Library { pub version: String } } /// /// let nix_version = pkg_config::probe_library("nix-store-c").unwrap().version; /// emit_version_cfg(&nix_version, &["2.26", "2.33.0pre", "2.33"]); /// ``` /// /// Emits `nix_at_least="2.26"` and `nix_at_least="2.33.0pre"` for version 2.33.0pre, /// usable as `#[cfg(nix_at_least = "2.26")]`. pub fn emit_version_cfg(nix_version: &str, relevant_versions: &[&str]) { // Declare the known versions for cargo check-cfg let versions = relevant_versions .iter() .map(|v| format!("\"{}\"", v)) .collect::>() .join(","); println!( "cargo:rustc-check-cfg=cfg(nix_at_least,values({}))", versions ); let nix_version = parse_version(nix_version); for version_str in relevant_versions { let version = parse_version(version_str); if nix_version >= version { println!("cargo:rustc-cfg=nix_at_least=\"{}\"", version_str); } } } /// Parse a Nix version string into a comparable tuple `(major, minor, patch)`. /// /// Pre-release versions (containing `"pre"`) get patch = -1, sorting before stable releases. /// Omitted patch defaults to 0. /// /// # Examples /// /// ``` /// use nix_bindings_util::nix_version::parse_version; /// /// assert_eq!(parse_version("2.26"), (2, 26, 0)); /// assert_eq!(parse_version("2.33.0pre"), (2, 33, -1)); /// assert_eq!(parse_version("2.33"), (2, 33, 0)); /// assert_eq!(parse_version("2.33.1"), (2, 33, 1)); /// /// // Pre-release versions sort before stable /// assert!(parse_version("2.33.0pre") < parse_version("2.33")); /// ``` pub fn parse_version(version_str: &str) -> (u32, u32, i32) { let parts = version_str.split('.').collect::>(); let major = parts[0].parse::().unwrap(); let minor = parts[1].parse::().unwrap(); let patch = if parts.get(2).is_some_and(|s| s.contains("pre")) { -1i32 } else { parts .get(2) .and_then(|s| s.parse::().ok()) .unwrap_or(0) }; (major, minor, patch) } #[cfg(test)] mod tests { use super::*; #[test] fn test_parse_version() { assert_eq!(parse_version("2.26"), (2, 26, 0)); assert_eq!(parse_version("2.33.0pre"), (2, 33, -1)); assert_eq!(parse_version("2.33"), (2, 33, 0)); assert_eq!(parse_version("2.33.1"), (2, 33, 1)); } #[test] fn test_version_ordering() { // Pre-release versions should sort before stable assert!(parse_version("2.33.0pre") < parse_version("2.33")); assert!(parse_version("2.33.0pre") < parse_version("2.33.0")); // Normal version ordering assert!(parse_version("2.26") < parse_version("2.33")); assert!(parse_version("2.33") < parse_version("2.33.1")); } }