invoice-cli 0.2.2

Beautiful invoices from the CLI — international, stateful, agent-friendly
Documentation
use std::path::PathBuf;

use directories::ProjectDirs;
use figment::{
    providers::{Env, Format, Toml},
    Figment,
};
use serde::{Deserialize, Serialize};

use crate::error::{AppError, Result};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config {
    pub default_issuer: Option<String>,
    pub default_template: String,
    pub open_pdf: bool,
    pub self_update: bool,
}

impl Default for Config {
    fn default() -> Self {
        Self {
            default_issuer: None,
            default_template: "vienna".into(),
            open_pdf: true,
            self_update: true,
        }
    }
}

fn dirs() -> Result<ProjectDirs> {
    ProjectDirs::from("com", "199-biotechnologies", "invoice")
        .ok_or_else(|| AppError::Config("could not resolve platform dirs".into()))
}

pub fn config_path() -> Result<PathBuf> {
    Ok(dirs()?.config_dir().join("config.toml"))
}

pub fn state_path() -> Result<PathBuf> {
    Ok(dirs()?.data_local_dir().to_path_buf())
}

pub fn db_path() -> Result<PathBuf> {
    Ok(state_path()?.join("invoice.db"))
}

pub fn assets_path() -> Result<PathBuf> {
    Ok(state_path()?.join("typst"))
}

pub fn load() -> Result<Config> {
    let path = config_path()?;
    let config = Figment::from(figment::providers::Serialized::defaults(Config::default()))
        .merge(Toml::file(&path))
        .merge(Env::prefixed("INVOICE_"))
        .extract::<Config>()
        .map_err(|e| AppError::Config(format!("{e}")))?;
    Ok(config)
}

pub fn ensure_dirs() -> Result<()> {
    let cfg = config_path()?.parent().map(|p| p.to_path_buf());
    if let Some(p) = cfg {
        std::fs::create_dir_all(&p)?;
    }
    std::fs::create_dir_all(state_path()?)?;
    std::fs::create_dir_all(assets_path()?)?;
    Ok(())
}