1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
use serde::{de::DeserializeOwned, Serialize};

/// A trait for persisted but not encrypted config source.
/// # Example
/// ```no_run
/// use encrypt_config::{Config, PersistSource};
/// use serde::{Deserialize, Serialize};
///
/// # #[cfg(feature = "secret")]
/// let mut config = Config::new("test");
/// # #[cfg(not(feature = "secret"))]
/// # let mut config = Config::new();
///
/// #[derive(Serialize, Deserialize, PartialEq, Debug)]
/// struct Foo(String);
///
/// struct PersistSourceImpl;
/// impl PersistSource for PersistSourceImpl {
///     type Value = Foo;
///     type Map = Vec<(String, Self::Value)>;
///
/// #   #[cfg(not(feature = "default_config_dir"))]
///     fn path(&self) -> std::path::PathBuf {
///         std::path::PathBuf::from("tests").join("persist.conf")
///     }
/// #
/// #    #[cfg(feature = "default_config_dir")]
/// #    fn source_name(&self) -> String {
/// #        "persist.conf".to_owned()
/// #    }
///
///     fn default(&self) -> Result<Self::Map, Box<dyn std::error::Error>> {
///         Ok(vec![("persist".to_owned(), Foo("persist".to_owned()))])
///     }
/// }
///
/// config.add_persist_source(PersistSourceImpl).unwrap();
/// let new_value = Foo("new persist".to_owned());
/// config.upgrade("persist", &new_value).unwrap();
/// assert_eq!(config.get::<_, Foo>("persist").unwrap(), new_value);
///
/// # #[cfg(feature = "secret")]
/// let mut config_new = Config::new("test");
/// # #[cfg(not(feature = "secret"))]
/// # let mut config_new = Config::new();
/// config_new.add_persist_source(PersistSourceImpl).unwrap(); // Read config from disk
/// assert_eq!(config_new.get::<_, Foo>("persist").unwrap(), new_value);
/// ```
#[cfg(feature = "persist")]
pub trait PersistSource {
    /// The type of the config value
    type Value: Serialize + DeserializeOwned;
    /// The type of the config map. It must be iterable, the first item of the tuple is the key, which should be `String` only.
    type Map: IntoIterator<Item = (String, Self::Value)>;

    /// The default config values from this source.
    /// This is the only way to add new config key-value pairs,
    /// because we cannot infer the source type(`normal`, `persist` and `secret`) of a new key after source merged into config if not so.
    fn default(&self) -> Result<Self::Map, Box<dyn std::error::Error>>;

    /// The name of the config file. Its parent directory is the OS' default config directory.
    /// It will be used as the file name if feature `default_config_dir` is on.
    #[cfg(feature = "default_config_dir")]
    fn source_name(&self) -> String;

    /// The path to persist the config file. Using the OS' default config directory.
    /// It will be used as the file path if feature `default_config_dir` is on.
    #[cfg(feature = "default_config_dir")]
    fn path(&self) -> std::path::PathBuf {
        dirs_next::config_dir()
            .expect("Default config dir unknown, please turn off feature `default_config_dir`")
            .join(self.source_name())
    }

    /// The path to persist the config file.
    #[cfg(not(feature = "default_config_dir"))]
    fn path(&self) -> std::path::PathBuf;
}