Skip to main content

age_setup/
config.rs

1use neuxcfg::Neuxcfg;
2use neuxcfg::NeuxcfgError;
3use neuxcfg::ProjectConfig;
4use toml::Value;
5
6/// The neuxcfg project name used by this crate.
7///
8/// All configuration is stored under this project identifier in the neuxcfg
9/// directory (typically `~/.config/neuxcfg/age-authenticator/`).
10pub const PROJECT_NAME: &str = "age-authenticator";
11
12/// Returns a new [`Neuxcfg`] instance using the system configuration directory.
13///
14/// This is a thin wrapper around [`Neuxcfg::new`] to avoid forcing users to
15/// depend directly on `neuxcfg`.
16///
17/// # Errors
18///
19/// Returns [`NeuxcfgError::ConfigDirNotFound`] if the system config directory
20/// cannot be determined.
21pub fn get_neuxcfg() -> Result<Neuxcfg, NeuxcfgError> {
22    Neuxcfg::new()
23}
24
25/// Initializes the configuration store for this crate.
26///
27/// Creates the neuxcfg root directory and global config if they do not exist,
28/// and ensures the `"age-authenticator"` project is registered.
29///
30/// Call this once at application startup before using other config functions.
31///
32/// # Errors
33///
34/// Returns [`NeuxcfgError`] variants for I/O failures or invalid project names.
35///
36/// # Examples
37///
38/// ```no_run
39/// use age_setup::config::init;
40///
41/// init()?;
42/// println!("Configuration store initialized.");
43/// # Ok::<(), neuxcfg::NeuxcfgError>(())
44/// ```
45pub fn init() -> Result<(), NeuxcfgError> {
46    let cfg = get_neuxcfg()?;
47    cfg.init()?;
48    if !cfg.project_exists(PROJECT_NAME)? {
49        cfg.add_project(PROJECT_NAME)?;
50    }
51    Ok(())
52}
53
54/// Loads the complete project configuration from the persistent store.
55///
56/// # Errors
57///
58/// Returns [`NeuxcfgError::ProjectNotFound`] if the project has not been
59/// initialized. Call [`init`] first.
60///
61/// # Examples
62///
63/// ```no_run
64/// use age_setup::config::{init, load_config};
65///
66/// init()?;
67/// let config = load_config()?;
68/// println!("Project path: {}", config.project.path);
69/// # Ok::<(), neuxcfg::NeuxcfgError>(())
70/// ```
71pub fn load_config() -> Result<ProjectConfig, NeuxcfgError> {
72    let cfg = get_neuxcfg()?;
73    cfg.get_project_config(PROJECT_NAME)
74}
75
76/// Overwrites the entire project configuration.
77///
78/// A backup of the previous configuration is created automatically by neuxcfg.
79///
80/// # Errors
81///
82/// Returns [`NeuxcfgError::ProjectNotFound`] if the project has not been
83/// initialized, or validation errors for invalid extra fields.
84///
85/// # Examples
86///
87/// ```no_run
88/// use age_setup::config::{init, load_config, save_config};
89///
90/// init()?;
91/// let mut config = load_config()?;
92/// config.project.path = "/new/path".into();
93/// save_config(&config)?;
94/// # Ok::<(), neuxcfg::NeuxcfgError>(())
95/// ```
96pub fn save_config(config: &ProjectConfig) -> Result<(), NeuxcfgError> {
97    let cfg = get_neuxcfg()?;
98    cfg.set_project_config(PROJECT_NAME, config)
99}
100
101/// Applies a partial update to the project configuration via deep merge.
102///
103/// Only the provided keys are modified; all other keys remain unchanged.
104///
105/// # Parameters
106///
107/// * `delta` – A [`toml::Value`] (usually a table) to merge into the existing config.
108///
109/// # Errors
110///
111/// Returns [`NeuxcfgError::ProjectNotFound`] if the project has not been
112/// initialized, or validation/TOML parse errors.
113///
114/// # Examples
115///
116/// ```no_run
117/// use age_setup::config::{init, update_config};
118/// use toml::toml;
119///
120/// init()?;
121/// let delta = toml! {
122///     [project]
123///     custom_setting = "enabled"
124/// };
125/// update_config(toml::Value::Table(delta))?;
126/// # Ok::<(), neuxcfg::NeuxcfgError>(())
127/// ```
128pub fn update_config(delta: toml::Value) -> Result<(), NeuxcfgError> {
129    let cfg = get_neuxcfg()?;
130    cfg.update_project_config(PROJECT_NAME, delta)
131}
132
133/// Sets a single extra field in the project configuration.
134///
135/// This is a convenience wrapper around [`load_config`] and [`save_config`].
136///
137/// # Parameters
138///
139/// * `key` – The extra field name (must not start with `_` or contain `.`).
140/// * `value` – A [`toml::Value`] to store.
141///
142/// # Errors
143///
144/// Returns [`NeuxcfgError`] variants from loading, validation, or saving.
145///
146/// # Examples
147///
148/// ```no_run
149/// use age_setup::config::{init, set_extra};
150/// use toml::Value;
151///
152/// init()?;
153/// set_extra("api_endpoint", Value::String("https://api.example.com".into()))?;
154/// # Ok::<(), neuxcfg::NeuxcfgError>(())
155/// ```
156pub fn set_extra(key: &str, value: Value) -> Result<(), NeuxcfgError> {
157    let mut config = load_config()?;
158    config.project.extra.insert(key.to_string(), value);
159    save_config(&config)
160}
161
162/// Retrieves a single extra field from the project configuration.
163///
164/// Returns `None` if the key does not exist.
165///
166/// # Parameters
167///
168/// * `key` – The extra field name to look up.
169///
170/// # Errors
171///
172/// Returns [`NeuxcfgError`] variants from loading the configuration.
173///
174/// # Examples
175///
176/// ```no_run
177/// use age_setup::config::{init, set_extra, get_extra};
178/// use toml::Value;
179///
180/// init()?;
181/// set_extra("theme", Value::String("dark".into()))?;
182/// let theme = get_extra("theme")?;
183/// assert_eq!(theme, Some(Value::String("dark".into())));
184/// # Ok::<(), neuxcfg::NeuxcfgError>(())
185/// ```
186pub fn get_extra(key: &str) -> Result<Option<Value>, NeuxcfgError> {
187    let config = load_config()?;
188    Ok(config.project.extra.get(key).cloned())
189}