Skip to main content

cloudiful_config/
lib.rs

1mod env;
2mod file;
3mod paths;
4
5use std::io;
6
7#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
8pub struct ReadOptions<'a> {
9    pub env_prefix: Option<&'a str>,
10}
11
12impl<'a> ReadOptions<'a> {
13    pub const fn with_env_prefix(env_prefix: &'a str) -> Self {
14        Self {
15            env_prefix: Some(env_prefix),
16        }
17    }
18}
19
20/// Save config to the platform-default `config.toml` for `app_name`.
21///
22/// On macOS, `stock` resolves to
23/// `~/Library/Application Support/stock/config.toml`.
24///
25/// ```rust,no_run
26/// use cloudiful_config::save;
27/// use serde::Serialize;
28///
29/// #[derive(Serialize)]
30/// struct AppConfig {
31///     port: u16,
32/// }
33///
34/// save("stock", AppConfig { port: 8080 }).unwrap();
35/// ```
36pub fn save<T>(app_name: &str, config: T) -> io::Result<()>
37where
38    T: serde::Serialize,
39{
40    let path = paths::default_config_path(app_name)?;
41    file::write_config(&path, &config, file::FileType::TOML)
42}
43
44/// Read config from the platform-default `config.toml` for `app_name`,
45/// creating the file from `T::default()` when it does not already exist.
46///
47/// Use [`ReadOptions`] to apply environment variable overrides after the file
48/// is loaded.
49///
50/// ```rust,no_run
51/// use cloudiful_config::{ReadOptions, read};
52/// use serde::{Deserialize, Serialize};
53///
54/// #[derive(Default, Deserialize, Serialize)]
55/// struct AppConfig {
56///     port: u16,
57/// }
58///
59/// let _config: AppConfig = read("stock", Some(ReadOptions::with_env_prefix("STOCK_"))).unwrap();
60/// ```
61pub fn read<T>(app_name: &str, options: Option<ReadOptions<'_>>) -> Result<T, io::Error>
62where
63    T: serde::de::DeserializeOwned + Default + serde::Serialize,
64{
65    let path = paths::default_config_path(app_name)?;
66    let config = if !path.is_file() {
67        let default_config = T::default();
68        file::write_config(&path, &default_config, file::FileType::TOML)?;
69        default_config
70    } else {
71        file::read_config(&path)?
72    };
73
74    match options.and_then(|options| options.env_prefix) {
75        Some(prefix) => env::apply_env_overrides(config, prefix),
76        None => Ok(config),
77    }
78}
79
80#[cfg(test)]
81mod tests;