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 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
//! # Quickstart //! //! Suppose you have the following file structure: //! //! ```text //! ├── config //! │ ├── .secrets.toml //! │ └── settings.toml //! └── your-executable //! ``` //! //! `settings.toml`: //! //! ```toml //! [default] //! pg.port = 5432 //! pg.host = 'localhost' //! //! [production] //! pg.host = 'db-0' //! ``` //! //! `.secrets.toml`: //! //! ```toml //! [default] //! pg.password = 'a password' //! //! [production] //! pg.password = 'a strong password' //! ``` //! //! Then, in your executable source (make sure to add `serde = { version = "1.0", //! features = ["derive"] }` to your dependencies): //! //! ```no_run //! use serde::Deserialize; //! use hydroconf::Hydroconf; //! //! #[derive(Debug, Deserialize)] //! struct Config { //! pg: PostgresConfig, //! } //! //! #[derive(Debug, Deserialize)] //! struct PostgresConfig { //! host: String, //! port: u16, //! password: String, //! } //! //! fn main() { //! let conf: Config = match Hydroconf::default().hydrate() { //! Ok(c) => c, //! Err(e) => { //! println!("could not read configuration: {:#?}", e); //! std::process::exit(1); //! } //! }; //! //! println!("{:#?}", conf); //! } //! ``` //! //! If you compile and execute the program (making sure the executable is in the //! same directory where the `config` directory is), you will see the following: //! //! ```sh //! $ ./your-executable //! Config { //! pg: PostgresConfig { //! host: "localhost", //! port: 5432, //! password: "a password" //! } //! } //! ``` //! //! Hydroconf will select the settings in the `[default]` table by default. If you //! set `ENV_FOR_HYDRO` to `production`, Hydroconf will overwrite them with the //! production ones: //! //! ```sh //! $ ENV_FOR_HYDRO=production ./your-executable //! Config { //! pg: PostgresConfig { //! host: "db-0", //! port: 5432, //! password: "a strong password" //! } //! } //! ``` //! //! Settings can always be overridden with environment variables: //! //! ```bash //! $ HYDRO_PG__PASSWORD="an even stronger password" ./your-executable //! Config { //! pg: PostgresConfig { //! host: "localhost", //! port: 5432, //! password: "an even stronger password" //! } //! } //! ``` //! # Environment variables //! There are two formats for the environment variables: //! //! 1. those that control how Hydroconf works have the form `*_FOR_HYDRO`; //! 2. those that override values in your configuration have the form `HYDRO_*`. //! //! For example, to specify where Hydroconf should look for the configuration //! files, you can set the variable `ROOT_PATH_FOR_HYDRO`. In that case, it's no //! longer necessary to place the binary in the same directory as the //! configuration. Hydroconf will search directly from the root path you specify. //! //! Here is a list of all the currently supported environment variables to //! configure how Hydroconf works: //! //! * `ROOT_PATH_FOR_HYDRO`: specifies the location from which Hydroconf should //! start searching configuration files. By default, Hydroconf will start from //! the directory that contains your executable; //! * `SETTINGS_FILE_FOR_HYDRO`: exact location of the main settings file; //! * `SECRETS_FILE_FOR_HYDRO`: exact location of the file containing secrets; //! * `ENV_FOR_HYDRO`: the environment to load after loading the `default` one //! (e.g. `development`, `testing`, `staging`, `production`, etc.). By default, //! Hydroconf will load the `development` environment, unless otherwise //! specified. //! * `ENVVAR_PREFIX_FOR_HYDRO`: the prefix of the environement variables holding //! your configuration -- see group number 2. above. By default it's `HYDRO` //! (note that you don't have to include the `_` separator, as that is added //! automatically); //! * `ENVVAR_NESTED_SEP_FOR_HYDRO`: the separator in the environment variables //! holding your configuration that signals a nesting point. By default it's `__` //! (double underscore), so if you set `HYDRO_REDIS__HOST=localhost`, Hydroconf //! will match it with the nested field `redis.host` in your configuration. //! //! # Hydroconf initialization //! You can create a new Hydroconf struct in two ways. //! //! The first one is to use the `Hydroconf::default()` method, which will use the //! default settings. The default constructor will attempt to load the settings //! from the environment variables (those in the form `*_FOR_HYDRO`), and if it //! doesn't find them it will use the default values. The alternative is to create //! a `HydroSettings` struct manually and pass it to `Hydroconf`: //! //! ```rust //! # use hydroconf::{Hydroconf, HydroSettings}; //! //! let hydro_settings = HydroSettings::default() //! .set_envvar_prefix("MYAPP".into()) //! .set_env("staging".into()); //! let hydro = Hydroconf::new(hydro_settings); //! ``` //! //! Note that `HydroSettings::default()` will still try to load the settings from //! the environment before you overwrite them. //! //! # The hydration process //! ## 1. Configuration loading //! When you call `Hydroconf::hydrate()`, Hydroconf starts looking for your //! configuration files and if it finds them, it loads them. The search starts from //! `HydroSettings.root_path`; if the root path is not defined, Hydroconf will use //! `std::env::current_exe()`. From this path, Hydroconf generates all the possible //! candidates by walking up the directory tree, also searching in the `config` //! subfolder at each level. For example, if the root path is //! `/home/user/www/api-server/dist`, Hydroconf will try the following paths, in //! this order: //! //! 1. `/home/user/www/api-server/dist/config` //! 2. `/home/user/www/api-server/dist` //! 3. `/home/user/www/api-server/config` //! 4. `/home/user/www/api-server` //! 5. `/home/user/www/config` //! 6. `/home/user/www` //! 7. `/home/user/config` //! 8. `/home/user` //! 9. `/home/config` //! 10. `/home` //! 11. `/config` //! 12. `/` //! //! In each directory, Hydroconf will search for the files //! `settings.{toml,json,yaml,ini,hjson}` and //! `.secrets.{toml,json,yaml,ini,hjson}`. As soon as one of those (or both) are //! found, the search stops and Hydroconf won't search the remaining upper levels. //! //! ## 2. Merging //! In this step, Hydroconf merges the values from the different environments //! from the configuration files discovered in the previous step. Hydroconf //! first checks if an environment called `default` exists: in that case those //! values are selected first. Then, it checks if the environment specified for //! Hydroconf (`ENV_FOR_HYDRO`, or "development" if not specified) exists and in //! that case it selects those values and merges them with the existing ones. //! //! ## 3. `.env` file overrides //! In this step Hydroconf starts from the root path (the same one from step 1), //! and walks the filesystem upward in search of an `.env` file. If it finds //! one, it parses it and merges those values with the existing ones. //! //! ## 4. Environment variables overrides //! In this step Hydroconf merges the values from all environment variables that //! you defined with the Hydro prefix (`HYDRO_` by default, as explained in the //! [previous section](#environment-variables)). //! //! ## 5. Deserialization //! Finally, Hydroconf tries to deserialize the configuration into the return //! type you specify, which should be your configuration struct. //! //! # Best practices //! In order to keep your configuration simple, secure and effective, Hydroconf //! makes it easy for you to follow these best practices: //! 1. keep the non-secret values inside `config/settings.{toml,yaml,json,...}` //! separated by environment; in particular, define a "default" environment //! which contains the base settings, and specialize it in all the additional //! environments that you need (e.g. "development", "testing", "staging", //! "production", etc.); //! 2. keep the secret values inside `config/.secrets.{toml,yaml,json,...}` //! separated by environment and exclude this file from version control; //! 3. define the environment variable `ENV_FOR_DYNACONF` to specify which //! environment should be loaded (besides the "default" one, which is always //! loaded first); //! 4. if you want to override some values, or specify some secret values which //! are not in the secret file, define the environment variables `HYDRO_*` //! (or use a custom prefix and define `ENVVAR_PREFIX_FOR_HYDRO`). mod env; mod hydro; mod settings; mod sources; mod utils; pub use hydro::{Config, ConfigError, Environment, File, Hydroconf}; pub use settings::HydroSettings; pub use sources::FileSources;