aboutsummaryrefslogtreecommitdiff
path: root/src/lib.rs
blob: 95e09f52b81a5bfd409d32b8e75db953cf7c6bd1 (plain)
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
//! `fondant` is a macro based library to take the boilerplate out of
//! configuration handling. Most of `fondant` is based off the `confy` crate.
//!
//! `fondant` adds a couple of extra features:
//!
//!  - support for json, yaml and toml
//!  - support for custom config paths
//!  - support for custom config file names
//!
//!  ### Sample usage
//!
//! ```
//! // the struct has to derive Serialize, Deserialize and Default
//! #[derive(Configure, Serialize, Deserialize, Default)]
//! #[config_file = "config.toml"]
//! // |
//! // `-- this attribute sets the file name to "config.toml"
//! // `-- the file format to "toml"
//! // `-- the file path to "default" (read the notes below)
//! struct AppConfig {
//!     version: u32,
//!     port: u32,
//!     username: String,
//! }
//!
//! fn main() {
//!     // use `load` (associated method) to load the config file
//!     let mut conf = AppConfig::load().unwrap();
//!
//!     // do stuff with conf
//!     conf.version = 2;
//!
//!     // call `store` to save changes
//!     conf.store().unwrap();
//! }
//! ```
//! **Notes**:  
//!  - `load` returns `Default::default` if the config file is not present, and stores
//!  a serialized `Default::default` at the specified path
//!  - the "default" config path varies by platform:
//!     * GNU/Linux: `$XDG_CONFIG_HOME/my_cool_crate/config.toml` (follows xdg spec)
//!     * MacOS: `$HOME/Library/Preferences/my_cool_crate/config.toml`
//!     * Windows: `{FOLDERID_RoamingAppData}\_project_path_\config`
//!
//! ### Customization
//!
//! Set your own filename, for ex.: `apprc`
//!
//! ```
//! #[derive(Configure, Serialize, Deserialize, Default)]
//! #[config_file = "apprc.toml"]
//! struct AppConfig {
//!     // -- snip --
//! }
//! // effective path: $XDG_CONFIG_HOME/my_cool_crate/apprc.toml
//! // effective format: toml
//! ```
//!
//! Change file format to `yaml`, by changing the file extension.
//! Supported extensions are `yaml`, `toml`, `json`:
//! ```
//! #[derive(Configure, Serialize, Deserialize, Default)]
//! #[config_file = "config.yaml"]
//! struct AppConfig {
//!     // -- snip --
//! }
//! // effective path: $XDG_CONFIG_HOME/my_cool_crate/config.yaml
//! // effective format: yaml
//! ```
//!
//! Override the default config path, for ex.: the home directory
//! (it is not recommended to override config path):
//! ```
//! #[derive(Configure, Serialize, Deserialize, Default)]
//! #[config_file = "~/.apprc.json"]
//! struct AppConfig {
//!     // -- snip --
//! }
//! // effective path: $HOME/.apprc.json
//! // effective format: json
//! ```

/// Re-exporting crates that fondant_derive depends on.
///
///
/// Unfortunately, this seems to be the only way to bring crates into
/// scope from a proc-macro crate
/// This module should not bother you
pub mod fondant_exports {
    pub use directories::{ProjectDirs, UserDirs};
    pub use serde::{de::DeserializeOwned, Serialize};
    pub use serde_json;
    pub use serde_yaml;
    use std::path::{Path, PathBuf};
    pub use toml;
    pub fn expand_tilde<P: AsRef<Path>>(path: P) -> PathBuf {
        let p = path.as_ref();
        if p.starts_with("~") {
            if p == Path::new("~") {
                return UserDirs::new().unwrap().home_dir().to_path_buf();
            } else {
                let mut h = UserDirs::new().unwrap().home_dir().to_path_buf();
                h.push(p.strip_prefix("~/").unwrap());
                return h;
            }
        }
        return p.to_path_buf();
    }
}

use serde::{de::DeserializeOwned, Serialize};
#[derive(Debug)]
/// Errors that `load` and `store` can result in
pub enum FondantError {
    /// Occurs when the home dir is not accessible.
    /// You should probably `panic!` when this is thrown.
    InvalidHomeDir,

    /// Invalid toml/yaml/json config.
    ConfigParseError,

    /// Invalid permissions to create config dir.
    /// Might occur when you set config dir to, say, `/etc/config.toml` and run without superuser.
    DirCreateErr(std::io::Error),
    LoadError,
    FileWriteError,
    FileReadError,
    FileOpenError,
}

/// Derive this trait on a struct to mark it as a 'configuration' struct.
pub trait Configure: Serialize + DeserializeOwned + Default {
    fn load() -> Result<Self, FondantError>;
    fn store(&self) -> Result<(), FondantError>;
}

pub use fondant_derive::Configure;