mod cli;
mod operations {
pub mod build;
pub mod sync;
}
mod helpers;
use crate::cli::get_matches;
use anyhow::{anyhow, Result};
use std::{borrow, path::PathBuf};
const REPO_NAME: &str = env!("CARGO_PKG_NAME");
fn main() -> Result<()> {
let matches = get_matches();
let data_path_result: Result<PathBuf> =
if let Some(data_dir_path) = matches.get_one::<String>("data-dir") {
replace_tilde_slash_with_home(data_dir_path)
} else {
Ok(dirs::data_dir()
.ok_or_else(|| anyhow!("Error getting data directory"))?
.join(format!("tinted-theming/{REPO_NAME}")))
};
let data_path = data_path_result?;
let data_schemes_path = data_path.join("schemes");
let schemes_path_result: Result<PathBuf> =
if let Some(schemes_dir) = matches.get_one::<String>("schemes-dir") {
let schemes_path = PathBuf::from(schemes_dir);
if !schemes_path.exists() {
return Err(anyhow!(
"The provided schemes path does not exist: {schemes_dir}"
));
}
replace_tilde_slash_with_home(schemes_dir)
} else {
Ok(data_path.join("schemes"))
};
let schemes_path = schemes_path_result?;
match matches.subcommand() {
Some(("build", sub_matches)) => {
let ignores = {
let mut matches = sub_matches
.get_many::<String>("ignore")
.unwrap_or_default()
.cloned()
.collect::<Vec<String>>();
if matches.is_empty() {
matches = vec![
"**/*.md".to_string(),
"**/.*".to_string(),
"**/LICENSE".to_string(),
];
}
matches
};
let sync = sub_matches
.get_one::<bool>("sync")
.is_some_and(ToOwned::to_owned);
let is_quiet = sub_matches
.get_one::<bool>("quiet")
.is_some_and(ToOwned::to_owned);
let template_dir = sub_matches
.get_one::<String>("template-dir")
.cloned()
.ok_or_else(|| anyhow!("template-dir is required"))?;
let template_path = PathBuf::from(template_dir);
if sync {
operations::sync::sync(&data_schemes_path, is_quiet)?;
}
operations::build::build(&template_path, &schemes_path, &ignores, is_quiet)?;
}
Some(("sync", sub_matches)) => {
let is_quiet: bool = sub_matches
.get_one::<bool>("quiet")
.is_some_and(borrow::ToOwned::to_owned);
operations::sync::sync(&data_schemes_path, is_quiet)?;
}
_ => {
println!("Basic usage: {REPO_NAME} apply <SCHEME_NAME>");
println!("For more information try --help");
}
}
Ok(())
}
fn replace_tilde_slash_with_home(path_str: &str) -> Result<PathBuf> {
let trimmed_path_str = path_str.trim();
if trimmed_path_str.starts_with("~/") {
dirs::home_dir().map_or_else(
|| Err(anyhow!("Unable to determine a home directory for \"{trimmed_path_str}\", please use an absolute path instead")),
|home_dir|
Ok(PathBuf::from(trimmed_path_str.replacen(
"~/",
format!("{}/", home_dir.display()).as_str(),
1,
)))
)
} else {
Ok(PathBuf::from(trimmed_path_str))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_replace_tilde_slash_with_home_expands_tilde() {
let result = replace_tilde_slash_with_home("~/some/path").unwrap();
let home = dirs::home_dir().unwrap();
assert_eq!(result, home.join("some/path"));
}
#[test]
fn test_replace_tilde_slash_with_home_absolute_path() {
let result = replace_tilde_slash_with_home("/absolute/path").unwrap();
assert_eq!(result, PathBuf::from("/absolute/path"));
}
#[test]
fn test_replace_tilde_slash_with_home_relative_path() {
let result = replace_tilde_slash_with_home("relative/path").unwrap();
assert_eq!(result, PathBuf::from("relative/path"));
}
#[test]
fn test_replace_tilde_slash_with_home_trims_whitespace() {
let result = replace_tilde_slash_with_home(" ~/some/path ").unwrap();
let home = dirs::home_dir().unwrap();
assert_eq!(result, home.join("some/path"));
}
#[test]
fn test_replace_tilde_slash_with_home_tilde_only_no_slash() {
let result = replace_tilde_slash_with_home("~notapath").unwrap();
assert_eq!(result, PathBuf::from("~notapath"));
}
}