1
1
//! Information about the user's Nix installation
2
2
use serde:: { Deserialize , Serialize } ;
3
- use std:: fmt;
3
+ use std:: { fmt, sync :: OnceLock } ;
4
4
use tokio:: sync:: OnceCell ;
5
5
6
6
use crate :: { command:: NixCmd , config:: NixConfig , env:: NixEnv , version:: NixVersion } ;
7
7
use regex:: Regex ;
8
8
9
+ static INSTALLATION_TYPE_PATTERNS : OnceLock < Vec < ( Regex , NixInstallationType ) > > = OnceLock :: new ( ) ;
10
+
9
11
/// Type of Nix installation
10
- #[ derive( Debug , Clone , PartialEq , Eq , Serialize , Deserialize ) ]
12
+ #[ derive( Debug , Clone , Copy , PartialEq , Eq , Serialize , Deserialize ) ]
11
13
pub enum NixInstallationType {
12
14
/// Official Nix installation
13
15
Official ,
@@ -25,6 +27,44 @@ impl fmt::Display for NixInstallationType {
25
27
}
26
28
27
29
impl NixInstallationType {
30
+ /// Get or initialize the compiled regex patterns
31
+ fn get_patterns ( ) -> & ' static Vec < ( Regex , NixInstallationType ) > {
32
+ INSTALLATION_TYPE_PATTERNS . get_or_init ( || {
33
+ let pattern_strings = [
34
+ (
35
+ r"^nix \(Determinate Nix [\d.]+\) (\d+)\.(\d+)\.(\d+)$" ,
36
+ NixInstallationType :: DeterminateSystems ,
37
+ ) ,
38
+ (
39
+ r"^nix \(Nix\) (\d+)\.(\d+)\.(\d+)$" ,
40
+ NixInstallationType :: Official ,
41
+ ) ,
42
+ ( r"^(\d+)\.(\d+)\.(\d+)$" , NixInstallationType :: Official ) ,
43
+ ] ;
44
+
45
+ let mut compiled_patterns = Vec :: new ( ) ;
46
+ for ( pattern_str, installation_type) in pattern_strings {
47
+ // If regex compilation fails, we'll panic at startup which is acceptable
48
+ let regex = Regex :: new ( pattern_str) . expect ( "Invalid regex pattern" ) ;
49
+ compiled_patterns. push ( ( regex, installation_type) ) ;
50
+ }
51
+ compiled_patterns
52
+ } )
53
+ }
54
+
55
+ /// Detect installation type from a version string
56
+ fn from_version_str ( version_str : & str ) -> Self {
57
+ let patterns = Self :: get_patterns ( ) ;
58
+ for ( regex, installation_type) in patterns {
59
+ if regex. is_match ( version_str) {
60
+ return * installation_type;
61
+ }
62
+ }
63
+
64
+ // Default to Official if no pattern matches
65
+ NixInstallationType :: Official
66
+ }
67
+
28
68
/// Detect the installation type by examining `nix --version` output
29
69
async fn detect ( ) -> Result < Self , NixInfoError > {
30
70
let cmd = NixCmd :: default ( ) ;
@@ -33,31 +73,10 @@ impl NixInstallationType {
33
73
cmd. arg ( "--version" ) ;
34
74
} )
35
75
. await
36
- . map_err ( |e | NixInfoError :: NixCmdError ( crate :: command :: NixCmdError :: CmdError ( e ) ) ) ?;
76
+ . map_err ( |_ | NixInfoError :: InstallationTypeDetectionError ) ?;
37
77
let version_str = String :: from_utf8_lossy ( & output) . trim ( ) . to_string ( ) ;
38
78
39
- let patterns = [
40
- (
41
- r"^nix \(Determinate Nix [\d.]+\) (\d+)\.(\d+)\.(\d+)$" ,
42
- NixInstallationType :: DeterminateSystems ,
43
- ) ,
44
- (
45
- r"^nix \(Nix\) (\d+)\.(\d+)\.(\d+)$" ,
46
- NixInstallationType :: Official ,
47
- ) ,
48
- ( r"^(\d+)\.(\d+)\.(\d+)$" , NixInstallationType :: Official ) ,
49
- ] ;
50
-
51
- for ( pattern, installation_type) in patterns {
52
- let re =
53
- Regex :: new ( pattern) . map_err ( |_| NixInfoError :: InstallationTypeDetectionError ) ?;
54
- if re. is_match ( & version_str) {
55
- return Ok ( installation_type) ;
56
- }
57
- }
58
-
59
- // Default to Official if no pattern matches
60
- Ok ( NixInstallationType :: Official )
79
+ Ok ( Self :: from_version_str ( & version_str) )
61
80
}
62
81
}
63
82
@@ -77,27 +96,7 @@ mod tests {
77
96
] ;
78
97
79
98
for ( version_str, expected_type) in test_cases {
80
- let patterns = [
81
- (
82
- r"^nix \(Determinate Nix [\d.]+\) (\d+)\.(\d+)\.(\d+)$" ,
83
- NixInstallationType :: DeterminateSystems ,
84
- ) ,
85
- (
86
- r"^nix \(Nix\) (\d+)\.(\d+)\.(\d+)$" ,
87
- NixInstallationType :: Official ,
88
- ) ,
89
- ( r"^(\d+)\.(\d+)\.(\d+)$" , NixInstallationType :: Official ) ,
90
- ] ;
91
-
92
- let mut detected_type = NixInstallationType :: Official ; // default
93
- for ( pattern, installation_type) in patterns {
94
- let re = Regex :: new ( pattern) . unwrap ( ) ;
95
- if re. is_match ( version_str) {
96
- detected_type = installation_type;
97
- break ;
98
- }
99
- }
100
-
99
+ let detected_type = NixInstallationType :: from_version_str ( version_str) ;
101
100
assert_eq ! (
102
101
detected_type, expected_type,
103
102
"Failed for version string: '{}'" ,
0 commit comments