@@ -2,7 +2,7 @@ use std::borrow::Cow;
22use std:: fmt:: Write ;
33use std:: path:: { Path , PathBuf } ;
44
5- use anyhow:: { Context , Result } ;
5+ use anyhow:: { Context , Result , bail } ;
66use itertools:: Itertools ;
77use owo_colors:: OwoColorize ;
88use tempfile:: TempDir ;
@@ -12,10 +12,21 @@ use crate::cli::run::Selectors;
1212use crate :: config;
1313use crate :: git;
1414use crate :: git:: GIT_ROOT ;
15+ use crate :: hooks:: { BuiltinHooks , HookRegistry , MetaHooks } ;
1516use crate :: printer:: Printer ;
1617use crate :: store:: Store ;
1718use crate :: warn_user;
1819
20+ /// Categorizes the repo argument into different types.
21+ enum RepoType < ' a > {
22+ /// A git repository (local path or remote URL)
23+ Git { repo : & ' a str , rev : Option < & ' a str > } ,
24+ /// The special "builtin" keyword for builtin hooks
25+ Builtin ,
26+ /// The special "meta" keyword for meta hooks
27+ Meta ,
28+ }
29+
1930async fn get_head_rev ( repo : & Path ) -> Result < String > {
2031 let head_rev = git:: git_cmd ( "get head rev" ) ?
2132 . arg ( "rev-parse" )
@@ -148,51 +159,91 @@ pub(crate) async fn try_repo(
148159 warn_user ! ( "`--config` option is ignored when using `try-repo`" ) ;
149160 }
150161
151- let store = Store :: from_settings ( ) ?;
152- let tmp_dir = TempDir :: with_prefix_in ( "try-repo-" , store. scratch_path ( ) ) ?;
162+ // Categorize the repo argument (case-insensitive for special keywords)
163+ let repo_type = if repo. eq_ignore_ascii_case ( "builtin" ) {
164+ if rev. is_some ( ) {
165+ warn_user ! ( "`--ref` option is ignored for `builtin` repo" ) ;
166+ }
167+ RepoType :: Builtin
168+ } else if repo. eq_ignore_ascii_case ( "meta" ) {
169+ if rev. is_some ( ) {
170+ warn_user ! ( "`--ref` option is ignored for `meta` repo" ) ;
171+ }
172+ RepoType :: Meta
173+ } else {
174+ RepoType :: Git {
175+ repo : & repo,
176+ rev : rev. as_deref ( ) ,
177+ }
178+ } ;
153179
154- let ( repo_path, rev) = prepare_repo_and_rev ( & repo, rev. as_deref ( ) , tmp_dir. path ( ) )
155- . await
156- . context ( "Failed to determine repository and revision" ) ?;
180+ match repo_type {
181+ RepoType :: Builtin => {
182+ try_special_repo :: < BuiltinHooks > ( run_args, refresh, verbose, printer) . await
183+ }
184+ RepoType :: Meta => try_special_repo :: < MetaHooks > ( run_args, refresh, verbose, printer) . await ,
185+ RepoType :: Git { repo, rev } => {
186+ try_git_repo ( repo, rev, run_args, refresh, verbose, printer) . await
187+ }
188+ }
189+ }
190+
191+ /// Try hooks from a special repository (builtin or meta).
192+ async fn try_special_repo < H : HookRegistry > (
193+ run_args : crate :: cli:: RunArgs ,
194+ refresh : bool ,
195+ verbose : bool ,
196+ printer : Printer ,
197+ ) -> Result < ExitStatus > {
198+ let repo_name = H :: REPO_NAME ;
157199
200+ let store = Store :: from_settings ( ) ?;
201+ let tmp_dir = TempDir :: with_prefix_in ( "try-repo-" , store. scratch_path ( ) ) ?;
158202 let store = Store :: from_path ( tmp_dir. path ( ) ) . init ( ) ?;
159- let repo_clone_path = store
160- . clone_repo (
161- & config:: RemoteRepo :: new ( repo_path. to_string ( ) , rev. clone ( ) , vec ! [ ] ) ,
162- None ,
163- )
164- . await ?;
165203
166204 let selectors = Selectors :: load ( & run_args. includes , & run_args. skips , GIT_ROOT . as_ref ( ) ?) ?;
167205
168- let manifest = config:: read_manifest ( & repo_clone_path. join ( prek_consts:: MANIFEST_FILE ) ) ?;
169- let hooks_str = manifest
170- . hooks
171- . into_iter ( )
172- . filter ( |hook| selectors. matches_hook_id ( & hook. id ) )
173- . map ( |hook| format ! ( "{}- id: {}" , " " . repeat( 6 ) , hook. id) )
206+ // Filter hook IDs based on selectors
207+ let hook_ids: Vec < _ > = H :: all_ids ( )
208+ . filter ( |id| selectors. matches_hook_id ( id) )
209+ . collect ( ) ;
210+
211+ if hook_ids. is_empty ( ) {
212+ bail ! ( "No hooks matched the specified selectors for repo `{repo_name}`" ) ;
213+ }
214+
215+ let hooks_str = hook_ids
216+ . iter ( )
217+ . map ( |id| format ! ( "{}- id: {}" , " " . repeat( 6 ) , id) )
174218 . join ( "\n " ) ;
175219
176220 let config_str = indoc:: formatdoc! { r"
177221 repos:
178- - repo: {repo_path}
179- rev: {rev}
222+ - repo: {repo_name}
180223 hooks:
181224 {hooks_str}
182- " ,
183- repo_path = repo_path,
184- rev = rev,
185- hooks_str = hooks_str,
186- } ;
225+ " } ;
187226
188227 let config_file = tmp_dir. path ( ) . join ( prek_consts:: CONFIG_FILE ) ;
189228 fs_err:: tokio:: write ( & config_file, & config_str) . await ?;
190229
191230 writeln ! ( printer. stdout( ) , "{}" , "Using config:" . cyan( ) . bold( ) ) ?;
192231 write ! ( printer. stdout( ) , "{}" , config_str. dimmed( ) ) ?;
193232
233+ invoke_run ( & store, config_file, run_args, refresh, verbose, printer) . await
234+ }
235+
236+ /// Helper to call `crate::cli::run` with common arguments.
237+ async fn invoke_run (
238+ store : & Store ,
239+ config_file : PathBuf ,
240+ run_args : crate :: cli:: RunArgs ,
241+ refresh : bool ,
242+ verbose : bool ,
243+ printer : Printer ,
244+ ) -> Result < ExitStatus > {
194245 crate :: cli:: run (
195- & store,
246+ store,
196247 Some ( config_file) ,
197248 vec ! [ ] ,
198249 vec ! [ ] ,
@@ -213,3 +264,63 @@ pub(crate) async fn try_repo(
213264 )
214265 . await
215266}
267+
268+ /// Try hooks from a git repository (local path or remote URL).
269+ async fn try_git_repo (
270+ repo : & str ,
271+ rev : Option < & str > ,
272+ run_args : crate :: cli:: RunArgs ,
273+ refresh : bool ,
274+ verbose : bool ,
275+ printer : Printer ,
276+ ) -> Result < ExitStatus > {
277+ let store = Store :: from_settings ( ) ?;
278+ let tmp_dir = TempDir :: with_prefix_in ( "try-repo-" , store. scratch_path ( ) ) ?;
279+
280+ let ( repo_path, rev) = prepare_repo_and_rev ( repo, rev, tmp_dir. path ( ) )
281+ . await
282+ . context ( "Failed to determine repository and revision" ) ?;
283+
284+ let store = Store :: from_path ( tmp_dir. path ( ) ) . init ( ) ?;
285+ let repo_clone_path = store
286+ . clone_repo (
287+ & config:: RemoteRepo :: new ( repo_path. to_string ( ) , rev. clone ( ) , vec ! [ ] ) ,
288+ None ,
289+ )
290+ . await ?;
291+
292+ let selectors = Selectors :: load ( & run_args. includes , & run_args. skips , GIT_ROOT . as_ref ( ) ?) ?;
293+
294+ let manifest = config:: read_manifest ( & repo_clone_path. join ( prek_consts:: MANIFEST_FILE ) ) ?;
295+ let hook_ids: Vec < _ > = manifest
296+ . hooks
297+ . into_iter ( )
298+ . filter ( |hook| selectors. matches_hook_id ( & hook. id ) )
299+ . map ( |hook| hook. id )
300+ . collect ( ) ;
301+
302+ if hook_ids. is_empty ( ) {
303+ bail ! ( "No hooks matched the specified selectors for repo" ) ;
304+ }
305+
306+ let hooks_str = hook_ids
307+ . iter ( )
308+ . map ( |id| format ! ( "{}- id: {}" , " " . repeat( 6 ) , id) )
309+ . join ( "\n " ) ;
310+
311+ let config_str = indoc:: formatdoc! { r"
312+ repos:
313+ - repo: {repo_path}
314+ rev: {rev}
315+ hooks:
316+ {hooks_str}
317+ " } ;
318+
319+ let config_file = tmp_dir. path ( ) . join ( prek_consts:: CONFIG_FILE ) ;
320+ fs_err:: tokio:: write ( & config_file, & config_str) . await ?;
321+
322+ writeln ! ( printer. stdout( ) , "{}" , "Using config:" . cyan( ) . bold( ) ) ?;
323+ write ! ( printer. stdout( ) , "{}" , config_str. dimmed( ) ) ?;
324+
325+ invoke_run ( & store, config_file, run_args, refresh, verbose, printer) . await
326+ }
0 commit comments