Skip to main content

finance_core/
paths.rs

1// ═══════════════════════════════════════════════════════════════════════════
2// Paths — canonical locations for the Paperfoot accounting suite.
3//
4// Every finance-* CLI reads/writes state under one shared location so a
5// user can switch between invoice-cli / receipt-cli / ledger-cli and see the
6// same issuers, the same SQLite database, and the same config.toml.
7//
8// macOS:   ~/Library/Application Support/com.paperfoot.accounting/
9// Linux:   ~/.local/share/com.paperfoot.accounting/  (+ ~/.config/ for config)
10// Windows: %APPDATA%\paperfoot\accounting\
11// ═══════════════════════════════════════════════════════════════════════════
12
13use std::path::PathBuf;
14
15use directories::ProjectDirs;
16
17use crate::error::{CoreError, Result};
18
19/// Canonical paths for the accounting suite. Every shared CLI should resolve
20/// paths through this struct — never hardcode paths per-tool.
21#[derive(Debug, Clone)]
22pub struct Paths {
23    pub config_dir: PathBuf,
24    pub data_dir: PathBuf,
25}
26
27impl Paths {
28    /// Resolve and create (if missing) the shared accounting-suite paths.
29    pub fn resolve() -> Result<Self> {
30        let dirs = ProjectDirs::from("com", "paperfoot", "accounting")
31            .ok_or_else(|| CoreError::Path("could not resolve platform directories".into()))?;
32        let config_dir = dirs.config_dir().to_path_buf();
33        let data_dir = dirs.data_local_dir().to_path_buf();
34        std::fs::create_dir_all(&config_dir)?;
35        std::fs::create_dir_all(&data_dir)?;
36        Ok(Self {
37            config_dir,
38            data_dir,
39        })
40    }
41
42    pub fn config_file(&self) -> PathBuf {
43        self.config_dir.join("config.toml")
44    }
45
46    pub fn db_file(&self) -> PathBuf {
47        self.data_dir.join("accounting.db")
48    }
49
50    /// Directory for per-tool asset overrides (user-placed custom Typst
51    /// templates, receipt image store root, etc). Each tool decides its own
52    /// subdirectory name.
53    pub fn assets_dir(&self) -> PathBuf {
54        self.data_dir.join("assets")
55    }
56}