1010use std:: path:: { Path , PathBuf } ;
1111use std:: sync:: { LazyLock , RwLock } ;
1212
13- use figment:: providers:: { Env , Serialized } ;
13+ use figment:: providers:: Serialized ;
1414use figment:: { Figment , Profile , Provider , value:: Map } ;
15+ use prek_consts:: env_vars:: EnvVars ;
1516use serde:: { Deserialize , Serialize } ;
1617
1718/// Global settings instance, initialized lazily.
@@ -162,20 +163,84 @@ impl Provider for PyProjectProvider {
162163 }
163164}
164165
166+ /// Custom provider for PREK_* environment variables that handles
167+ /// special cases like comma-separated lists and boolean values.
168+ struct PrekEnvProvider ;
169+
170+ impl Provider for PrekEnvProvider {
171+ fn metadata ( & self ) -> figment:: Metadata {
172+ figment:: Metadata :: named ( "PREK_* environment variables" )
173+ }
174+
175+ fn data ( & self ) -> Result < Map < Profile , figment:: value:: Dict > , figment:: Error > {
176+ use figment:: value:: { Dict , Value } ;
177+
178+ let mut dict = Dict :: new ( ) ;
179+
180+ // PREK_SKIP or SKIP - comma-separated list
181+ if let Some ( val) = EnvVars :: var ( EnvVars :: PREK_SKIP )
182+ . ok ( )
183+ . or_else ( || EnvVars :: var ( EnvVars :: SKIP ) . ok ( ) )
184+ {
185+ let items: Vec < Value > = val
186+ . split ( ',' )
187+ . map ( |s| s. trim ( ) . to_string ( ) )
188+ . filter ( |s| !s. is_empty ( ) )
189+ . map ( Value :: from)
190+ . collect ( ) ;
191+ dict. insert ( "skip" . into ( ) , Value :: from ( items) ) ;
192+ }
193+
194+ // PREK_HOME or PRE_COMMIT_HOME (EnvVars::var handles the fallback)
195+ if let Ok ( val) = EnvVars :: var ( EnvVars :: PREK_HOME ) {
196+ dict. insert ( "home" . into ( ) , Value :: from ( val) ) ;
197+ }
198+
199+ if let Ok ( val) = EnvVars :: var ( EnvVars :: PREK_COLOR ) {
200+ dict. insert ( "color" . into ( ) , Value :: from ( val) ) ;
201+ }
202+
203+ // PREK_ALLOW_NO_CONFIG - boolish values (EnvVars::var handles PRE_COMMIT fallback)
204+ if let Some ( b) = EnvVars :: var_as_bool ( EnvVars :: PREK_ALLOW_NO_CONFIG ) {
205+ dict. insert ( "allow-no-config" . into ( ) , Value :: from ( b) ) ;
206+ }
207+
208+ // PREK_NO_CONCURRENCY - boolish values (EnvVars::var handles PRE_COMMIT fallback)
209+ if let Some ( b) = EnvVars :: var_as_bool ( EnvVars :: PREK_NO_CONCURRENCY ) {
210+ dict. insert ( "no-concurrency" . into ( ) , Value :: from ( b) ) ;
211+ }
212+
213+ if let Some ( b) = EnvVars :: var_as_bool ( EnvVars :: PREK_NO_FAST_PATH ) {
214+ dict. insert ( "no-fast-path" . into ( ) , Value :: from ( b) ) ;
215+ }
216+
217+ if let Ok ( val) = EnvVars :: var ( EnvVars :: PREK_UV_SOURCE ) {
218+ dict. insert ( "uv-source" . into ( ) , Value :: from ( val) ) ;
219+ }
220+
221+ if let Some ( b) = EnvVars :: var_as_bool ( EnvVars :: PREK_NATIVE_TLS ) {
222+ dict. insert ( "native-tls" . into ( ) , Value :: from ( b) ) ;
223+ }
224+
225+ if let Ok ( val) = EnvVars :: var ( EnvVars :: PREK_CONTAINER_RUNTIME ) {
226+ dict. insert ( "container-runtime" . into ( ) , Value :: from ( val) ) ;
227+ }
228+
229+ let mut map = Map :: new ( ) ;
230+ map. insert ( Profile :: Default , dict) ;
231+ Ok ( map)
232+ }
233+ }
234+
165235impl Settings {
166236 /// Initialize settings from the given working directory.
167237 ///
168238 /// This should be called early in `main()` before any settings are accessed.
169239 /// If not called, settings will use the current directory when first accessed.
170240 pub fn init ( working_dir : & Path ) -> Result < ( ) , figment:: Error > {
171241 let settings = Self :: discover ( working_dir) ?;
172-
173- let mut guard = SETTINGS . write ( ) . expect ( "settings lock poisoned" ) ;
174- * guard = settings;
175-
176- let mut init_guard = INITIALIZED . write ( ) . expect ( "initialized lock poisoned" ) ;
177- * init_guard = true ;
178-
242+ * SETTINGS . write ( ) . expect ( "settings lock poisoned" ) = settings;
243+ * INITIALIZED . write ( ) . expect ( "initialized lock poisoned" ) = true ;
179244 Ok ( ( ) )
180245 }
181246
@@ -187,15 +252,9 @@ impl Settings {
187252 cli_overrides : CliOverrides ,
188253 ) -> Result < ( ) , figment:: Error > {
189254 let figment = Self :: build_figment ( working_dir) . merge ( Serialized :: defaults ( cli_overrides) ) ;
190-
191255 let settings: Settings = figment. extract ( ) ?;
192-
193- let mut guard = SETTINGS . write ( ) . expect ( "settings lock poisoned" ) ;
194- * guard = settings;
195-
196- let mut init_guard = INITIALIZED . write ( ) . expect ( "initialized lock poisoned" ) ;
197- * init_guard = true ;
198-
256+ * SETTINGS . write ( ) . expect ( "settings lock poisoned" ) = settings;
257+ * INITIALIZED . write ( ) . expect ( "initialized lock poisoned" ) = true ;
199258 Ok ( ( ) )
200259 }
201260
@@ -214,25 +273,16 @@ impl Settings {
214273 let _ = Self :: init ( & cwd) ;
215274 }
216275 }
217-
218- let guard = SETTINGS . read ( ) . expect ( "settings lock poisoned" ) ;
219- guard. clone ( )
276+ SETTINGS . read ( ) . expect ( "settings lock poisoned" ) . clone ( )
220277 }
221278
222279 /// Build the figment for the given working directory.
223280 fn build_figment ( working_dir : & Path ) -> Figment {
224281 let mut figment = Figment :: new ( )
225282 // Lowest precedence: built-in defaults
226283 . merge ( Serialized :: defaults ( Settings :: default ( ) ) )
227- // Next: environment variables
228- . merge (
229- Env :: prefixed ( "PREK_" )
230- . map ( |key| {
231- // Convert PREK_FOO_BAR to foo-bar for serde
232- key. as_str ( ) . to_lowercase ( ) . replace ( '_' , "-" ) . into ( )
233- } )
234- . split ( "," ) , // Allow comma-separated lists for `skip`
235- ) ;
284+ // Next: environment variables (custom provider for special handling)
285+ . merge ( PrekEnvProvider ) ;
236286
237287 // Walk up to find pyproject.toml
238288 let mut current = Some ( working_dir) ;
@@ -349,7 +399,7 @@ color = "always"
349399 fn test_env_var_loading ( ) {
350400 figment:: Jail :: expect_with ( |jail| {
351401 jail. set_env ( "PREK_SKIP" , "hook1,hook2" ) ;
352- jail. set_env ( "PREK_NO_CONCURRENCY" , "true " ) ;
402+ jail. set_env ( "PREK_NO_CONCURRENCY" , "1 " ) ;
353403 jail. set_env ( "PREK_COLOR" , "never" ) ;
354404
355405 let settings = Settings :: discover ( jail. directory ( ) ) ?;
0 commit comments