use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::PathBuf;
const DEFAULT_DB_NAME: &str = "dojo";
const DEFAULT_DB_FILE: &str = "judo.db";
const DEFAULT_FG_COLOUR: &str = "#FCF1D5";
const DEFAULT_HL_COLOUR: &str = "#FFA69E";
const DEFAULT_BG_COLOUR: &str = "#002626";
#[derive(Deserialize, Serialize, Clone)]
pub struct Config {
pub default: String,
pub dbs: Vec<DBConfig>,
#[serde(default)]
pub colours: Theme,
}
#[derive(Deserialize, Serialize, Clone)]
pub struct DBConfig {
pub name: String,
pub connection_str: String,
}
#[derive(Deserialize, Serialize, Clone)]
pub struct Theme {
pub background: String,
pub foreground: String,
pub highlight: String,
}
impl Default for Theme {
fn default() -> Self {
Self {
background: DEFAULT_BG_COLOUR.to_string(),
foreground: DEFAULT_FG_COLOUR.to_string(),
highlight: DEFAULT_HL_COLOUR.to_string(),
}
}
}
impl Default for DBConfig {
fn default() -> Self {
let data_dir = dirs::data_dir().unwrap().join("judo");
std::fs::create_dir_all(&data_dir).unwrap();
let path = data_dir.join(DEFAULT_DB_FILE);
let connection_str = format!("sqlite:{}", path.display());
Self {
name: DEFAULT_DB_NAME.to_string(),
connection_str,
}
}
}
impl Default for Config {
fn default() -> Self {
Self {
default: DEFAULT_DB_NAME.to_string(),
dbs: vec![DBConfig::default()],
colours: Theme::default(),
}
}
}
impl Config {
pub fn write(&self, config_path: &PathBuf) -> Result<()> {
let toml_content =
toml::to_string_pretty(&self).with_context(|| "Failed to serialize judo.toml")?;
fs::write(config_path, toml_content).with_context(|| {
format!(
"Failed to write yomo.toml file to {}",
config_path.display()
)
})?;
Ok(())
}
pub fn read() -> Result<Self> {
let config_dir = dirs::config_dir().unwrap().join("judo");
let config_path = config_dir.join("judo.toml");
if !config_dir.exists() | !config_path.exists() {
std::fs::create_dir_all(&config_dir)
.with_context(|| "Failed to create config directory")?;
let config = Self::default();
config
.write(&config_path)
.with_context(|| "Failed to create config file")?;
return Ok(Self::default());
}
let judo_config: Config = toml::from_str(
&fs::read_to_string(config_path).with_context(|| "Failed to read into string")?,
)
.with_context(|| "Failed to serialize into struct")?;
Ok(judo_config)
}
pub fn foreground(&self) -> &str {
&self.colours.foreground
}
pub fn highlight(&self) -> &str {
&self.colours.highlight
}
pub fn background(&self) -> &str {
&self.colours.background
}
pub fn get_db_by_name(self, name: String) -> Result<DBConfig> {
let dbs = self.dbs;
for db in dbs {
if db.name == name {
return Ok(db);
}
}
eprintln!("ERROR: No database found called '{}'", name);
std::process::exit(exitcode::DATAERR)
}
pub fn get_default(&self) -> Result<DBConfig> {
let matching_dbs: Vec<_> = self
.dbs
.iter()
.filter(|db| db.name == self.default)
.collect();
match matching_dbs.len() {
0 => anyhow::bail!("Default database '{}' not found", self.default),
1 => Ok(matching_dbs[0].clone()),
_ => anyhow::bail!("Multiple databases with name '{}' found", self.default),
}
}
}