Skip to main content

libc_abi_tools/
version.rs

1//! Numeric versions, used for glibc releases (`2.17`) and symbol versions
2//! (`GLIBC_2.2.5` without its prefix).
3use std::{fmt, str::FromStr};
4
5/// Numeric ABI version. Historical symbol versions may precede our target floor.
6#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
7///
8/// Parsed from two or three dot-separated numbers (`2.17`, `2.2.5`); a missing
9/// third number is zero. It displays without a zero patch.
10pub struct Version {
11    /// The first number.
12    pub major: u32,
13    /// The second number.
14    pub minor: u32,
15    /// The third number; zero when absent.
16    pub patch: u32,
17}
18
19impl Version {
20    /// Validate a glibc release directory (as opposed to a symbol version).
21    ///
22    /// # Errors
23    ///
24    /// Fails unless the version is 2.x with x at least 17 and no patch number.
25    pub fn validate_release(self) -> Result<Self, String> {
26        if self.major != 2 || self.minor < 17 || self.patch != 0 {
27            return Err(
28                "expected a glibc 2.x release starting at 2.17, without a patch component".into(),
29            );
30        }
31        Ok(self)
32    }
33}
34
35impl FromStr for Version {
36    type Err = String;
37
38    fn from_str(text: &str) -> Result<Self, String> {
39        let parts: Vec<_> = text.split('.').collect();
40        if !(2..=3).contains(&parts.len()) {
41            return Err(format!("invalid numeric version {text:?}"));
42        }
43        let number = |s: &str| -> Result<u32, String> {
44            if s.is_empty() || !s.bytes().all(|b| b.is_ascii_digit()) {
45                return Err(format!("invalid numeric version {text:?}"));
46            }
47            s.parse()
48                .map_err(|_| format!("version component too large: {text:?}"))
49        };
50        Ok(Self {
51            major: number(parts[0])?,
52            minor: number(parts[1])?,
53            patch: if parts.len() == 3 {
54                number(parts[2])?
55            } else {
56                0
57            },
58        })
59    }
60}
61
62impl fmt::Display for Version {
63    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64        write!(f, "{}.{}", self.major, self.minor)?;
65        if self.patch != 0 {
66            write!(f, ".{}", self.patch)?;
67        }
68        Ok(())
69    }
70}