1#![warn(missing_docs)]
4#![cfg(target_os = "macos")]
5
6use std::fmt::Display;
7
8use sysctl::{Ctl, Sysctl, SysctlError};
9
10#[derive(Debug)]
12#[non_exhaustive]
13pub enum Error {
14 NotPosix,
16 BadMacModel,
18 Sysctl(SysctlError),
20 ParseOsVersion,
22 Many(Vec<Self>),
24}
25
26impl Display for Error {
27 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 match self {
29 Error::NotPosix => write!(f, "your macOS version is not compliant with POSIX, it is recommended to downgrade your macOS to a version prior to 14.4"),
30 Error::BadMacModel => write!(f, "you have a bad taste, sell your Mac immediately and get a MacBook Pro (13-inch, M1, 2020)"),
31 Error::Sysctl(err) => write!(f, "sysctl error: {}", err),
32 Error::ParseOsVersion => write!(f, "your macOS version looks weird and can't be parsed"),
33 Error::Many(errs) => {
34 write!(f, "multiple errors: ")?;
35 for err in errs {
36 write!(f, "{}, ", err)?;
37 }
38 Ok(())
39 },
40 }
41 }
42}
43
44impl From<SysctlError> for Error {
45 #[inline]
46 fn from(value: SysctlError) -> Self {
47 Self::Sysctl(value)
48 }
49}
50
51impl std::error::Error for Error {
52 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
53 match self {
54 Error::Sysctl(err) => Some(err),
55 _ => None,
56 }
57 }
58}
59
60const HW_MODEL: &str = "hw.model";
61const KERN_OSPRODUCTVERSION: &str = "kern.osproductversion";
62
63const PULP_MACHINE: &str = "MacBookPro16,1";
65
66pub fn check() -> Result<(), Error> {
73 let mut errs: Vec<Error> = Vec::with_capacity(2);
74 if let Err(err) = check_posix() {
75 errs.push(err);
76 }
77 if let Err(err) = check_machine() {
78 errs.push(err);
79 }
80 if errs.is_empty() {
81 Ok(())
82 } else if let Some(err) = errs.pop().filter(|_| errs.is_empty()) {
83 Err(err)
84 } else {
85 Err(Error::Many(errs))
86 }
87}
88
89fn check_posix() -> Result<(), Error> {
91 let ctl = Ctl::new(KERN_OSPRODUCTVERSION)?;
92 let ver_str = ctl.value_string()?;
93 let ver_split = ver_str.split('.');
94 let mut is_sonoma = false;
95 for num in ver_split {
96 if !is_sonoma {
97 match num.parse::<usize>().map_err(|_| Error::ParseOsVersion)? {
98 ..=13 => return Ok(()),
99 14 => is_sonoma = true,
100 _ => return Err(Error::NotPosix),
101 }
102 } else if let ..=3 = num.parse::<usize>().map_err(|_| Error::ParseOsVersion)? {
103 return Ok(());
104 } else {
105 return Err(Error::NotPosix);
106 }
107 }
108
109 Err(Error::ParseOsVersion)
111}
112
113fn check_machine() -> Result<(), Error> {
114 let ctl = Ctl::new(HW_MODEL)?;
115 if ctl.value_string()? == PULP_MACHINE {
116 Err(Error::BadMacModel)
117 } else {
118 Ok(())
119 }
120}
121
122#[cfg(test)]
123mod test {
124 #[test]
125 fn test_decency() {
126 assert!(crate::check().is_ok())
127 }
128}