use std::fs;
use std::path::PathBuf;
use crate::config::Config as ThconConfig;
use crate::operation::Operation;
use crate::themeable::{ConfigError, ConfigState, Themeable};
use crate::AppConfig;
use crate::Disableable;
use anyhow::{Context, Result};
use log::{debug, error};
use regex::{Captures, Regex};
use serde::Deserialize;
#[derive(Debug, Deserialize, Disableable, AppConfig)]
pub struct _Config {
light: String,
dark: String,
config: Option<String>,
#[serde(default)]
disabled: bool,
}
#[derive(Debug, Deserialize)]
pub struct Alacritty {}
impl Themeable for Alacritty {
fn config_state(&self, config: &ThconConfig) -> ConfigState {
ConfigState::with_manual_config(config.alacritty.as_ref().map(|c| c.inner.as_ref()))
}
fn switch(&self, config: &ThconConfig, operation: &Operation) -> Result<()> {
let config = match self.config_state(config) {
ConfigState::NoDefault => {
return Err(ConfigError::RequiresManualConfig("alacritty").into())
}
ConfigState::Default => unreachable!(),
ConfigState::Disabled => return Ok(()),
ConfigState::Enabled => config.alacritty.as_ref().unwrap().unwrap_inner_left(),
};
let theme = match operation {
Operation::Darken => &config.dark,
Operation::Lighten => &config.light,
};
let alacritty_yaml = match alacritty_config() {
Some(path) => path,
None => {
let couldnt_find = String::from("Couldn't find alacritty.yml");
let message = match &config.config {
Some(_path) => couldnt_find,
None => format!("{}; consider adding `config` property to `[alacritty]` section of `thcon.toml`", couldnt_find),
};
error!("{}", message);
return Ok(());
}
};
debug!(
"Reading/writing alacritty.yml at {}",
alacritty_yaml.display()
);
match fs::read_to_string(&alacritty_yaml)
.with_context(|| format!("Unable to read settings from {}", &alacritty_yaml.display()))
{
Ok(settings) => {
let theme_regex = Regex::new(
r#"^(?P<prefix>"?colors"?\s*:\s*"?)(?P<v>[^\s]+)(?P<suffix>"?,?\s*#\s*thcon:replace-line)"#,
)?;
let modified_lines: Vec<String> = settings
.lines()
.map(|line| {
theme_regex
.replace(line, |caps: &Captures| {
format!("{}*{}{}", &caps["prefix"], theme, &caps["suffix"])
})
.into_owned()
})
.collect();
let settings = modified_lines.join("\n");
fs::write(&alacritty_yaml, settings).with_context(|| {
format!("Unable to write settings to {}", &alacritty_yaml.display())
})
}
Err(e) => {
error!("Unable to read settings: {}", e);
Err(e)
}
}
}
}
#[cfg(not(windows))]
fn alacritty_config() -> Option<PathBuf> {
use std::env;
xdg::BaseDirectories::with_prefix("alacritty")
.ok()
.and_then(|xdg| xdg.find_config_file("alacritty.yml"))
.or_else(|| {
xdg::BaseDirectories::new()
.ok()
.and_then(|fallback| fallback.find_config_file("alacritty.yml"))
})
.or_else(|| {
if let Ok(home) = env::var("HOME") {
let fallback = PathBuf::from(&home).join(".config/alacritty/alacritty.yml");
if fallback.exists() {
return Some(fallback);
}
let fallback = PathBuf::from(&home).join(".alacritty.yml");
if fallback.exists() {
return Some(fallback);
}
}
None
})
}
#[cfg(windows)]
fn alacritty_config() -> Option<PathBuf> {
dirs::config_dir()
.map(|path| path.join("alacritty\\alacritty.yml"))
.filter(|new| new.exists())
}