mir_analyzer/
php_version.rs1use std::fmt;
8use std::str::FromStr;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
11pub struct PhpVersion {
12 major: u8,
13 minor: u8,
14}
15
16impl PhpVersion {
17 pub const LATEST: PhpVersion = PhpVersion::new(8, 5);
18
19 pub const fn new(major: u8, minor: u8) -> Self {
20 Self { major, minor }
21 }
22
23 pub const fn major(self) -> u8 {
24 self.major
25 }
26
27 pub const fn minor(self) -> u8 {
28 self.minor
29 }
30}
31
32impl Default for PhpVersion {
33 fn default() -> Self {
34 Self::LATEST
35 }
36}
37
38impl fmt::Display for PhpVersion {
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 write!(f, "{}.{}", self.major, self.minor)
41 }
42}
43
44#[derive(Debug, thiserror::Error)]
45#[error("invalid PHP version `{0}`: expected `MAJOR.MINOR` (e.g. `8.2`)")]
46pub struct ParsePhpVersionError(pub String);
47
48impl FromStr for PhpVersion {
49 type Err = ParsePhpVersionError;
50
51 fn from_str(s: &str) -> Result<Self, Self::Err> {
52 let mut parts = s.trim().split('.');
53 let major = parts
54 .next()
55 .and_then(|p| p.parse::<u8>().ok())
56 .ok_or_else(|| ParsePhpVersionError(s.to_string()))?;
57 let minor = parts.next().and_then(|p| p.parse::<u8>().ok()).unwrap_or(0);
58 Ok(Self::new(major, minor))
60 }
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66
67 #[test]
68 fn parses_major_minor() {
69 assert_eq!("8.2".parse::<PhpVersion>().unwrap(), PhpVersion::new(8, 2));
70 }
71
72 #[test]
73 fn parses_major_minor_patch() {
74 assert_eq!(
75 "8.3.7".parse::<PhpVersion>().unwrap(),
76 PhpVersion::new(8, 3)
77 );
78 }
79
80 #[test]
81 fn parses_major_only() {
82 assert_eq!("7".parse::<PhpVersion>().unwrap(), PhpVersion::new(7, 0));
83 }
84
85 #[test]
86 fn rejects_garbage() {
87 assert!("x.y".parse::<PhpVersion>().is_err());
88 assert!("".parse::<PhpVersion>().is_err());
89 }
90
91 #[test]
92 fn ordered_by_major_then_minor() {
93 assert!(PhpVersion::new(8, 1) < PhpVersion::new(8, 2));
94 assert!(PhpVersion::new(7, 4) < PhpVersion::new(8, 0));
95 }
96
97 #[test]
98 fn displays_as_major_dot_minor() {
99 assert_eq!(PhpVersion::new(8, 3).to_string(), "8.3");
100 }
101}