mod cli;
mod config;
mod constants;
mod operations {
pub mod apply;
pub mod build;
pub mod config;
pub mod current;
pub mod cycle;
pub mod generate_scheme;
pub mod info;
pub mod init;
pub mod install;
pub mod list;
pub mod sync;
pub mod update;
}
mod utils;
use crate::cli::{build_cli, get_matches};
use anyhow::{anyhow, Context, Result};
use clap::Command;
use clap_complete::{generate, Generator, Shell};
use config::{CONFIG_FILE_NAME, ORG_NAME};
use constants::{CUSTOM_SCHEMES_DIR_NAME, REPO_DIR, REPO_NAME, SCHEMES_REPO_NAME};
use operations::generate_scheme;
use std::path::PathBuf;
use std::string::String;
use tinted_builder::{SchemeSystem, SchemeVariant};
use utils::{ensure_directory_exists, replace_tilde_slash_with_home};
use xdg::BaseDirectories;
#[allow(clippy::too_many_lines)]
fn main() -> Result<()> {
let matches = get_matches();
let config_path_result: Result<PathBuf> = matches.get_one::<String>("config").map_or_else(
|| {
let xdg_dirs = BaseDirectories::with_prefix(format!("{ORG_NAME}/{REPO_NAME}"));
xdg_dirs.map_or_else(
|_| Err(anyhow!("err")),
|xdg_dirs| {
xdg_dirs
.place_config_file(CONFIG_FILE_NAME)
.context(format!(
"Unable to create XDG_HOME/{ORG_NAME}/{REPO_NAME}/{CONFIG_FILE_NAME}",
))
},
)
},
|config_file_path| replace_tilde_slash_with_home(config_file_path),
);
let config_path = config_path_result?;
let data_path: PathBuf = if let Some(data_file_path) = matches.get_one::<String>("data-dir") {
replace_tilde_slash_with_home(data_file_path)?
} else {
let xdg_dirs = BaseDirectories::with_prefix(format!("{ORG_NAME}/{REPO_NAME}"));
if let Ok(xdg_dirs) = xdg_dirs {
xdg_dirs.get_data_home()
} else {
return Err(anyhow!("err"));
}
};
let data_repo_path = data_path.join(REPO_DIR);
ensure_directory_exists(&data_path)
.with_context(|| format!("Failed to create data directory at {}", data_path.display()))?;
ensure_directory_exists(&data_repo_path).with_context(|| {
format!(
"Failed to create config directory at {}",
data_repo_path.display()
)
})?;
match matches.subcommand() {
Some(("build", sub_matches)) => {
if let Some(template_dir) = sub_matches.get_one::<String>("template-dir") {
let schemes_repo_path = data_path.join(format!("{REPO_DIR}/{SCHEMES_REPO_NAME}"));
let template_path = PathBuf::from(template_dir);
operations::build::build(&template_path, &schemes_repo_path)?;
}
}
Some(("current", sub_matches)) => {
let property_name = sub_matches
.get_one::<String>("property_name")
.map(String::as_str)
.unwrap_or_default();
operations::current::current(&data_path, property_name)?;
}
Some(("config", sub_matches)) => {
let data_dir_path_flag = sub_matches.get_flag("data-dir-path");
let config_path_flag = sub_matches.get_flag("config-path");
operations::config::config(
&config_path,
&data_path,
config_path_flag,
data_dir_path_flag,
)?;
}
Some(("generate-completion", sub_matches)) => {
if let Some(generator) = sub_matches.get_one::<Shell>("shell_name") {
let mut cmd = build_cli();
print_completions(*generator, &mut cmd);
return Ok(());
}
}
Some(("info", sub_matches)) => {
let is_custom = sub_matches
.get_one::<bool>("custom-schemes")
.is_some_and(ToOwned::to_owned);
let is_exhaustive_list = sub_matches
.get_one::<bool>("all")
.is_some_and(ToOwned::to_owned);
let scheme_name_option = sub_matches.get_one::<String>("scheme-name");
operations::info::info(
&data_path,
scheme_name_option,
is_custom,
is_exhaustive_list,
)?;
}
Some(("init", sub_matches)) => {
let is_verbose = sub_matches
.get_one::<bool>("verbose")
.is_some_and(ToOwned::to_owned);
operations::init::init(&config_path, &data_path, is_verbose)?;
}
Some(("list", sub_matches)) => {
let is_custom = sub_matches
.get_one::<bool>("custom-schemes")
.is_some_and(ToOwned::to_owned);
let is_json = sub_matches
.get_one::<bool>("json")
.is_some_and(ToOwned::to_owned);
operations::list::list(&data_path, is_custom, is_json)?;
}
Some(("apply", sub_matches)) => {
if let Some(theme) = sub_matches.get_one::<String>("scheme-name") {
let is_quiet = sub_matches
.get_one::<bool>("quiet")
.is_some_and(ToOwned::to_owned);
let scheme_name = theme.as_str();
operations::apply::apply(&config_path, &data_path, scheme_name, is_quiet, None)
.with_context(|| format!("Failed to apply theme \"{scheme_name}\""))?;
}
}
Some(("cycle", sub_matches)) => {
let is_quiet = sub_matches
.get_one::<bool>("quiet")
.is_some_and(ToOwned::to_owned);
operations::cycle::cycle(&config_path, &data_path, is_quiet, None)
.context("Failed to cycle to your next preferred theme")?;
}
Some(("install", sub_matches)) => {
let is_quiet = sub_matches
.get_one::<bool>("quiet")
.is_some_and(ToOwned::to_owned);
operations::install::install(&config_path, &data_path, is_quiet)?;
}
Some(("update", sub_matches)) => {
let is_quiet = sub_matches
.get_one::<bool>("quiet")
.is_some_and(ToOwned::to_owned);
operations::update::update(&config_path, &data_path, is_quiet)?;
}
Some(("sync", sub_matches)) => {
let is_quiet = sub_matches
.get_one::<bool>("quiet")
.is_some_and(ToOwned::to_owned);
operations::sync::sync(&config_path, &data_path, is_quiet)?;
}
Some(("generate-scheme", sub_matches)) => {
let slug_default = "tinty-generated".to_string();
let slug = sub_matches
.get_one::<String>("slug")
.unwrap_or(&slug_default);
let name_default = "Tinty Generated".to_string();
let description = sub_matches
.get_one::<String>("description")
.map(String::from);
let name = sub_matches
.get_one::<String>("name")
.unwrap_or(&name_default);
let author_default = "Tinty".to_string();
let author = sub_matches
.get_one::<String>("author")
.unwrap_or(&author_default);
let image_path = sub_matches.get_one::<String>("image_path").map_or_else(
|| Err(anyhow!("No image file specified")),
|content| {
PathBuf::from(content)
.canonicalize()
.with_context(|| "Invalid image file supplied")
},
)?;
let system = match sub_matches.get_one::<String>("system").map(String::as_str) {
Some("base24") => SchemeSystem::Base24,
_ => SchemeSystem::Base16,
};
let variant = match sub_matches.get_one::<String>("variant").map(String::as_str) {
Some("light") => SchemeVariant::Light,
_ => SchemeVariant::Dark,
};
let outfile_path_option = {
let custom_scheme_path = data_path.join(CUSTOM_SCHEMES_DIR_NAME);
let save = sub_matches.get_one::<bool>("save").unwrap_or(&false);
ensure_directory_exists(custom_scheme_path.join("base16")).with_context(|| {
format!(
"Failed to create custom scheme directory at {}",
data_path.display()
)
})?;
ensure_directory_exists(custom_scheme_path.join("base24")).with_context(|| {
format!(
"Failed to create custom scheme directory at {}",
data_path.display()
)
})?;
ensure_directory_exists(custom_scheme_path.join("tinted8")).with_context(|| {
format!(
"Failed to create custom scheme directory at {}",
data_path.display()
)
})?;
if *save {
let filename = format!("{slug}.yaml");
Some(custom_scheme_path.join(format!("{system}/{filename}")))
} else {
None
}
};
generate_scheme::generate_scheme(
image_path,
outfile_path_option,
author.clone(),
description,
name.clone(),
slug.clone(),
system,
variant,
)?;
}
_ => {
println!("Basic usage: {REPO_NAME} apply <SCHEME_NAME>");
println!("For more information try --help");
}
}
Ok(())
}
fn print_completions<G: Generator>(gen: G, cmd: &mut Command) {
generate(gen, cmd, cmd.get_name().to_string(), &mut std::io::stdout());
}