tinted-builder-rust 0.15.0

Simple Base16, Base24 and Tinted8 compliant rendering of mustache templates
Documentation
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");

/// Expands a leading `~/` to the current user's home directory.
///
/// Returns the original input as a `PathBuf` if not prefixed with `~/`.
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))
    }
}

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 = sub_matches
                .get_many::<String>("ignore")
                .unwrap_or_default()
                .cloned()
                .collect::<Vec<String>>();
            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(())
}