@@ -329,6 +329,68 @@ impl<'de> Deserialize<'de> for Stages {
329329 }
330330}
331331
332+ /// Controls whether filenames are appended to a hook's command line.
333+ #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
334+ pub ( crate ) enum PassFilenames {
335+ /// Pass all matching filenames (default). Corresponds to `pass_filenames:
336+ /// true`.
337+ All ,
338+ /// Pass no filenames. Corresponds to `pass_filenames: false`.
339+ None ,
340+ /// Pass at most `n` filenames per invocation. Corresponds to
341+ /// `pass_filenames: n`.
342+ Limited ( std:: num:: NonZeroUsize ) ,
343+ }
344+
345+ impl < ' de > Deserialize < ' de > for PassFilenames {
346+ fn deserialize < D > ( deserializer : D ) -> Result < Self , D :: Error >
347+ where
348+ D : Deserializer < ' de > ,
349+ {
350+ struct PassFilenamesVisitor ;
351+
352+ impl serde:: de:: Visitor < ' _ > for PassFilenamesVisitor {
353+ type Value = PassFilenames ;
354+
355+ fn expecting ( & self , f : & mut std:: fmt:: Formatter ) -> std:: fmt:: Result {
356+ f. write_str ( "a boolean or a positive integer" )
357+ }
358+
359+ fn visit_bool < E : DeError > ( self , v : bool ) -> Result < PassFilenames , E > {
360+ Ok ( if v {
361+ PassFilenames :: All
362+ } else {
363+ PassFilenames :: None
364+ } )
365+ }
366+
367+ fn visit_u64 < E : DeError > ( self , v : u64 ) -> Result < PassFilenames , E > {
368+ let n = usize:: try_from ( v)
369+ . ok ( )
370+ . and_then ( std:: num:: NonZeroUsize :: new)
371+ . ok_or_else ( || {
372+ E :: custom (
373+ "pass_filenames must be a positive integer; use `false` to pass no filenames" ,
374+ )
375+ } ) ?;
376+ Ok ( PassFilenames :: Limited ( n) )
377+ }
378+
379+ fn visit_i64 < E : DeError > ( self , v : i64 ) -> Result < PassFilenames , E > {
380+ if v <= 0 {
381+ return Err ( E :: custom (
382+ "pass_filenames must be a positive integer; use `false` to pass no filenames" ,
383+ ) ) ;
384+ }
385+ #[ allow( clippy:: cast_sign_loss) ]
386+ self . visit_u64 ( v as u64 )
387+ }
388+ }
389+
390+ deserializer. deserialize_any ( PassFilenamesVisitor )
391+ }
392+ }
393+
332394/// Common hook options.
333395#[ derive( Debug , Clone , Default , Deserialize ) ]
334396#[ cfg_attr( feature = "schemars" , derive( schemars:: JsonSchema ) ) ]
@@ -363,7 +425,7 @@ pub(crate) struct HookOptions {
363425 pub fail_fast : Option < bool > ,
364426 /// Append filenames that would be checked to the hook entry as arguments.
365427 /// Default is true.
366- pub pass_filenames : Option < bool > ,
428+ pub pass_filenames : Option < PassFilenames > ,
367429 /// A description of the hook. For metadata only.
368430 pub description : Option < String > ,
369431 /// Run the hook on a specific version of the language.
@@ -2033,4 +2095,52 @@ mod tests {
20332095 let config = serde_saphyr:: from_str :: < Config > ( yaml) . unwrap ( ) ;
20342096 insta:: assert_debug_snapshot!( config) ;
20352097 }
2098+
2099+ #[ test]
2100+ fn pass_filenames_zero_is_rejected ( ) {
2101+ let yaml = indoc:: indoc! { r"
2102+ repos:
2103+ - repo: local
2104+ hooks:
2105+ - id: invalid-pass-filenames-zero
2106+ name: invalid pass_filenames zero
2107+ entry: echo
2108+ language: system
2109+ pass_filenames: 0
2110+ " } ;
2111+ let result = serde_saphyr:: from_str :: < Config > ( yaml) ;
2112+ assert ! ( result. is_err( ) ) ;
2113+ }
2114+
2115+ #[ test]
2116+ fn pass_filenames_negative_is_rejected ( ) {
2117+ let yaml = indoc:: indoc! { r"
2118+ repos:
2119+ - repo: local
2120+ hooks:
2121+ - id: invalid-pass-filenames-negative
2122+ name: invalid pass_filenames negative
2123+ entry: echo
2124+ language: system
2125+ pass_filenames: -1
2126+ " } ;
2127+ let result = serde_saphyr:: from_str :: < Config > ( yaml) ;
2128+ assert ! ( result. is_err( ) ) ;
2129+ }
2130+
2131+ #[ test]
2132+ fn pass_filenames_string_is_rejected ( ) {
2133+ let yaml = indoc:: indoc! { r#"
2134+ repos:
2135+ - repo: local
2136+ hooks:
2137+ - id: invalid-pass-filenames-string
2138+ name: invalid pass_filenames string
2139+ entry: echo
2140+ language: system
2141+ pass_filenames: "foo"
2142+ "# } ;
2143+ let result = serde_saphyr:: from_str :: < Config > ( yaml) ;
2144+ assert ! ( result. is_err( ) ) ;
2145+ }
20362146}
0 commit comments