1use std::path::PathBuf;
2
3use directories::ProjectDirs;
4use figment::{
5 providers::{Env, Format, Toml},
6 Figment,
7};
8use serde::{Deserialize, Serialize};
9
10use crate::error::{AppError, Result};
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct Config {
14 pub default_issuer: Option<String>,
15 pub default_template: String,
16 pub open_pdf: bool,
17 pub self_update: bool,
18}
19
20impl Default for Config {
21 fn default() -> Self {
22 Self {
23 default_issuer: None,
24 default_template: "vienna".into(),
25 open_pdf: true,
26 self_update: true,
27 }
28 }
29}
30
31fn dirs() -> Result<ProjectDirs> {
32 ProjectDirs::from("com", "199-biotechnologies", "invoice")
33 .ok_or_else(|| AppError::Config("could not resolve platform dirs".into()))
34}
35
36pub fn config_path() -> Result<PathBuf> {
37 Ok(dirs()?.config_dir().join("config.toml"))
38}
39
40pub fn state_path() -> Result<PathBuf> {
41 Ok(dirs()?.data_local_dir().to_path_buf())
42}
43
44pub fn db_path() -> Result<PathBuf> {
45 Ok(state_path()?.join("invoice.db"))
46}
47
48pub fn assets_path() -> Result<PathBuf> {
49 Ok(state_path()?.join("typst"))
50}
51
52pub fn load() -> Result<Config> {
53 let path = config_path()?;
54 let config = Figment::from(figment::providers::Serialized::defaults(Config::default()))
55 .merge(Toml::file(&path))
56 .merge(Env::prefixed("INVOICE_"))
57 .extract::<Config>()
58 .map_err(|e| AppError::Config(format!("{e}")))?;
59 Ok(config)
60}
61
62pub fn ensure_dirs() -> Result<()> {
63 let cfg = config_path()?.parent().map(|p| p.to_path_buf());
64 if let Some(p) = cfg {
65 std::fs::create_dir_all(&p)?;
66 }
67 std::fs::create_dir_all(state_path()?)?;
68 std::fs::create_dir_all(assets_path()?)?;
69 Ok(())
70}