1use std::collections::HashMap;
2
3mod config;
4pub use config::NO_CONFIG_FILE_FOUND;
5
6mod git;
7pub use git::NO_CONFIG_FILE_FOUND_ERROR_CODE;
8
9pub mod hooks;
10
11pub fn init_directory<F, G, H>(
12 run_command: F,
13 write_file: G,
14 file_exists: H,
15 target_directory: Option<&str>,
16 hook_file_skip_list: Vec<&str>,
17) -> Result<(), String>
18where
19 F: Fn(
20 &str,
21 Option<&str>,
22 bool,
23 Option<&HashMap<String, String>>,
24 ) -> Result<Option<String>, Option<String>>,
25 G: Fn(&str, &str, bool) -> Result<(), String>,
26 H: Fn(&str) -> Result<bool, ()>,
27{
28 let root_directory_path = match git::get_root_directory_path(&run_command, target_directory) {
29 Ok(Some(path)) => path,
30 _ => return Err(String::from("Failure determining git repo root directory")),
31 };
32 if git::setup_hooks(
33 &run_command,
34 &write_file,
35 &root_directory_path,
36 &hook_file_skip_list,
37 )
38 .is_err()
39 {
40 return Err(String::from("Unable to create git hooks"));
41 };
42
43 if config::create_default_config_file(&write_file, &file_exists, &root_directory_path).is_err()
44 {
45 return Err(String::from("Unable to create config file"));
46 }
47
48 Ok(())
49}
50
51pub fn init<F, G, H>(
52 run_command: F,
53 write_file: G,
54 file_exists: H,
55 hook_file_skip_list: Vec<&str>,
56) -> Result<(), String>
57where
58 F: Fn(
59 &str,
60 Option<&str>,
61 bool,
62 Option<&HashMap<String, String>>,
63 ) -> Result<Option<String>, Option<String>>,
64 G: Fn(&str, &str, bool) -> Result<(), String>,
65 H: Fn(&str) -> Result<bool, ()>,
66{
67 init_directory(
68 &run_command,
69 &write_file,
70 &file_exists,
71 None,
72 hook_file_skip_list,
73 )
74}
75
76pub fn run<F, G, H, I>(
77 run_command: F,
78 file_exists: G,
79 read_file: H,
80 log: I,
81 hook_name: &str,
82 args: Option<String>,
83) -> Result<(), Option<String>>
84where
85 F: Fn(
86 &str,
87 Option<&str>,
88 bool,
89 Option<&HashMap<String, String>>,
90 ) -> Result<Option<String>, Option<String>>,
91 G: Fn(&str) -> Result<bool, ()>,
92 H: Fn(&str) -> Result<String, ()>,
93 I: Fn(&str, bool),
94{
95 let root_directory_path = match git::get_root_directory_path(&run_command, None) {
96 Ok(Some(path)) => path,
97 _ => {
98 return Err(Some(String::from(
99 "Failure determining git repo root directory",
100 )));
101 }
102 };
103
104 let config_file_contents =
105 config::get_config_file_contents(read_file, file_exists, &root_directory_path).map_err(
106 |e| {
107 if e == config::NO_CONFIG_FILE_FOUND {
108 Some(e)
109 } else {
110 Some(String::from("Failed to parse config file"))
111 }
112 },
113 )?;
114
115 let log_details = config::get_log_setting(&config_file_contents);
116 let (script, env_vars) = match (
117 config::get_hook_script(&config_file_contents, hook_name),
118 args,
119 ) {
120 (Ok(script), None) => (script, None),
121 (Ok(script), Some(a)) => (
122 script.replace("%rh!", &a),
123 Some(
124 vec![("RUSTY_HOOKS_GIT_PARAMS".to_owned(), a)]
125 .into_iter()
126 .collect::<HashMap<String, String>>(),
127 ),
128 ),
129 (Err(err), _) => {
130 if err == config::MISSING_CONFIG_KEY {
131 return Ok(());
132 }
133 return Err(Some(String::from("Invalid rusty-hooks config file")));
134 }
135 };
136
137 let message = format!(
138 "[rusty-hooks] Found configured hook: {hook_name}\n[rusty-hooks] Running command: {script}\n"
139 );
140 log(&message, log_details);
141
142 run_command(
143 &script,
144 Some(&root_directory_path),
145 log_details,
146 env_vars.as_ref(),
147 )
148 .map(|_| ())
149}
150
151#[cfg(test)]
152mod tests;