1
1
mod command;
2
+
2
3
use command:: command_stdout_lossy;
4
+ use std:: io;
3
5
4
6
pub fn version ( ) -> String {
5
- let version = run_sw_vers ( ) . unwrap_or ( String :: from ( "N/A" ) ) ;
7
+ let version = MacosVersion :: new ( )
8
+ . map ( |version| version. version ( ) )
9
+ . unwrap_or ( String :: from ( "N/A" ) ) ;
6
10
format ! ( "macOS {}" , version)
7
11
}
8
12
9
13
pub fn short_version ( ) -> String {
10
- let version = run_sw_vers ( )
11
- . and_then ( parse_short_version_output)
12
- . map ( |( major, minor) | format ! ( "{}.{}" , major, minor) )
14
+ let version = MacosVersion :: new ( )
15
+ . map ( |version| version. short_version ( ) )
13
16
. unwrap_or ( String :: from ( "N/A" ) ) ;
14
17
format ! ( "macOS {}" , version)
15
18
}
@@ -18,14 +21,77 @@ pub fn extra_metadata() -> impl Iterator<Item = (String, String)> {
18
21
std:: iter:: empty ( )
19
22
}
20
23
24
+ #[ derive( Debug , PartialEq ) ]
25
+ pub struct MacosVersion {
26
+ raw_version : String ,
27
+ major : u32 ,
28
+ minor : u32 ,
29
+ patch : u32 ,
30
+ }
31
+
32
+ impl MacosVersion {
33
+ pub fn new ( ) -> Result < MacosVersion , io:: Error > {
34
+ Self :: from_raw_version ( & run_sw_vers ( ) ?)
35
+ }
36
+
37
+ fn from_raw_version ( version_string : & str ) -> Result < MacosVersion , io:: Error > {
38
+ let ( major, minor, patch) = parse_version_output ( version_string) . ok_or ( io:: Error :: new (
39
+ io:: ErrorKind :: InvalidInput ,
40
+ "Failed to parse raw version string" ,
41
+ ) ) ?;
42
+ Ok ( MacosVersion {
43
+ raw_version : version_string. to_owned ( ) ,
44
+ major,
45
+ minor,
46
+ patch,
47
+ } )
48
+ }
49
+
50
+ /// Return the current version as a string (e.g. 14.2.1)
51
+ pub fn version ( & self ) -> String {
52
+ self . raw_version . clone ( )
53
+ }
54
+
55
+ /// Return the current version as a string (e.g. 14.2), not including the patch version
56
+ pub fn short_version ( & self ) -> String {
57
+ format ! ( "{}.{}" , self . major_version( ) , self . minor_version( ) )
58
+ }
59
+
60
+ pub fn major_version ( & self ) -> u32 {
61
+ self . major
62
+ }
63
+
64
+ pub fn minor_version ( & self ) -> u32 {
65
+ self . minor
66
+ }
67
+
68
+ pub fn patch_version ( & self ) -> u32 {
69
+ self . patch
70
+ }
71
+ }
72
+
21
73
/// Outputs a string in a format `$major.$minor.$patch`, e.g. `11.0.1`
22
- fn run_sw_vers ( ) -> Option < String > {
74
+ fn run_sw_vers ( ) -> io :: Result < String > {
23
75
command_stdout_lossy ( "sw_vers" , & [ "-productVersion" ] )
24
76
}
25
77
26
- fn parse_short_version_output ( output : String ) -> Option < ( u32 , u32 ) > {
78
+ fn parse_version_output ( output : & str ) -> Option < ( u32 , u32 , u32 ) > {
27
79
let mut parts = output. split ( '.' ) ;
28
80
let major = parts. next ( ) ?. parse ( ) . ok ( ) ?;
29
81
let minor = parts. next ( ) ?. parse ( ) . ok ( ) ?;
30
- Some ( ( major, minor) )
82
+ let patch = parts
83
+ . next ( )
84
+ . and_then ( |patch| patch. parse ( ) . ok ( ) )
85
+ . unwrap_or ( 0 ) ;
86
+ Some ( ( major, minor, patch) )
87
+ }
88
+
89
+ #[ test]
90
+ fn test_version_parsing ( ) {
91
+ // % sw_vers --productVersion
92
+ // 14.2.1
93
+ let version = MacosVersion :: from_raw_version ( "14.2.1" ) . expect ( "failed to parse version" ) ;
94
+ assert_eq ! ( version. major_version( ) , 14 ) ;
95
+ assert_eq ! ( version. minor_version( ) , 2 ) ;
96
+ assert_eq ! ( version. patch_version( ) , 1 ) ;
31
97
}
0 commit comments