use toml::Value;
const CONFIG_FILE_TEMPLATE: &str = "[hooks]
pre-commit = \"cargo test\"
[logging]
verbose = true
";
const DEFAULT_CONFIG_FILE_NAME: &str = ".rusty-hook.toml";
const CONFIG_FILE_NAMES: [&str; 2] = [DEFAULT_CONFIG_FILE_NAME, "rusty-hook.toml"];
pub const NO_CONFIG_FILE_FOUND: &str = "No config file found";
pub const MISSING_CONFIG_KEY: &str = "Missing config key";
fn find_config_file<F>(root_directory_path: &str, file_exists: F) -> Result<String, String>
where
F: Fn(&str) -> Result<bool, ()>,
{
for &config_file_name in CONFIG_FILE_NAMES.iter() {
let path = format!("{}/{}", root_directory_path, config_file_name);
match file_exists(&path) {
Ok(found) => {
if found {
return Ok(path);
}
}
Err(_) => {
return Err(String::from(
"Fatal error encountered while looking for existing config",
));
}
};
}
Ok(String::from(NO_CONFIG_FILE_FOUND))
}
pub fn create_default_config_file<F, G>(
write_file: F,
file_exists: G,
root_directory_path: &str,
) -> Result<(), String>
where
F: Fn(&str, &str, bool) -> Result<(), String>,
G: Fn(&str) -> Result<bool, ()>,
{
create_config_file(
&write_file,
&file_exists,
root_directory_path,
DEFAULT_CONFIG_FILE_NAME,
)
}
pub fn create_config_file<F, G>(
write_file: F,
file_exists: G,
root_directory_path: &str,
desired_config_file_name: &str,
) -> Result<(), String>
where
F: Fn(&str, &str, bool) -> Result<(), String>,
G: Fn(&str) -> Result<bool, ()>,
{
match find_config_file(root_directory_path, &file_exists) {
Ok(path) => {
if path != NO_CONFIG_FILE_FOUND {
return Ok(());
}
}
Err(_) => {
return Err(String::from(
"Fatal error encountered while looking for existing config",
));
}
};
let config_file = if CONFIG_FILE_NAMES
.iter()
.any(|n| n == &desired_config_file_name)
{
desired_config_file_name
} else {
DEFAULT_CONFIG_FILE_NAME
};
if write_file(
&format!("{}/{}", root_directory_path, config_file),
CONFIG_FILE_TEMPLATE,
false,
)
.is_err()
{
return Err(String::from("Failed to create config file"));
};
Ok(())
}
pub fn get_config_file_contents<F, G>(
read_file: F,
file_exists: G,
root_directory_path: &str,
) -> Result<String, String>
where
F: Fn(&str) -> Result<String, ()>,
G: Fn(&str) -> Result<bool, ()>,
{
let path = match find_config_file(root_directory_path, &file_exists) {
Ok(path) => {
if path == NO_CONFIG_FILE_FOUND {
return Err(String::from(NO_CONFIG_FILE_FOUND));
} else {
path
}
}
Err(_) => return Err(String::from(NO_CONFIG_FILE_FOUND)),
};
match read_file(&path) {
Ok(contents) => Ok(contents),
Err(_) => Err(String::from("Failure reading file")),
}
}
fn get_table_key_value_from_config(
config_contents: &str,
table: &str,
key: &str,
) -> Result<Value, String> {
let value = match config_contents.parse::<Value>() {
Ok(val) => val,
Err(_) => return Err(String::from("Error parsing config file")),
};
let config = value.as_table().unwrap();
if !config.contains_key(table) {
return Err(String::from("Missing config table"));
};
if !value[table].as_table().unwrap().contains_key(key) {
return Err(String::from(MISSING_CONFIG_KEY));
};
Ok(value[table][key].clone())
}
pub fn get_log_setting(config_contents: &str) -> bool {
match get_table_key_value_from_config(config_contents, "logging", "verbose") {
Err(_) => true,
Ok(value) => match value.as_bool() {
Some(setting) => setting,
None => true,
},
}
}
pub fn get_hook_script(config_contents: &str, hook_name: &str) -> Result<String, String> {
match get_table_key_value_from_config(config_contents, "hooks", hook_name) {
Err(err) => Err(err),
Ok(value) => match value.as_str() {
Some(script) => Ok(String::from(script)),
None => Err(String::from("Invalid hook config")),
},
}
}
#[cfg(test)]
#[path = "config_test.rs"]
mod config_tests;