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}