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
#![warn(missing_docs)]
#![deny(rustdoc::broken_intra_doc_links)]

//! This crate provides easy-to-use functionality for managing settings files
//! that are based on templates (e.g. `.env` and `.env.dist` files).
//!
//! This crate assumes that the files it manages are `key=value` pairs which it
//! understands. It will add keys to the settings if they exist in the template file
//! and optionally it'll remove keys from the settings file if they are absent from
//! the template file.
//!
//! ## Parsing files
//!
//! This example shows how you can easily read existing files into [`Settings`] struct's
//! with the function [`deserialize()`].
//! [`Settings`] are the foundation for any further processing by this crate.
//!
//! ```no_run
//! // read_file returns a std::io::Result<String>
//! let settings = renvy::read_file("./.env");
//! assert!(settings.is_ok());
//! if let Ok(settings) = settings {
//!     // renvy::deserialize consumes this String and returns an instance of renvy::Settings
//!     let settings: renvy::Settings = renvy::deserialize(&settings);
//!     println!("Number of settings read: {}", &settings.len());
//!     settings.iter().for_each(|(key, value)| {
//!         println!("{:?}: {:?}\n", key, value);
//!     });
//! } else {
//!     println!("Unable to read settings file!");
//! }
//!
//! // you can use the same function for reading settings and template files
//! let defaults = renvy::read_file("./.env.dist");
//! assert!(defaults.is_ok());
//! if let Ok(defaults) = defaults {
//!     // we're reusing the same data structure for defaults
//!     let defaults: renvy::Settings = renvy::deserialize(&defaults);
//!     println!("Number of defaults read: {}", &defaults.len());
//!     defaults.iter().for_each(|(key, value)| {
//!         println!("{:?}: {:?}\n", key, value);
//!     });
//! } else {
//!     println!("Unable to read defaults file!");
//! }
//! ```
//!
//! ## Updating settings based on template
//!
//! [`merge()`] allows you to update settings based on an existing template.
//! New keys in the template will be added to the settings with the default
//! value given in the template.
//!
//! ```
//! // settings file contains 1 key-value pair
//! let settings = renvy::Settings::from([("domain".into(), Some("https://benjaminsattler.net".into()))]);
//!
//! // defaults file contains 1 other key-value pair
//! let defaults = renvy::Settings::from([("port".into(), Some("433".into()))]);
//!
//! // merging defaults with settings will result in a new object merge::settings
//! // that contains 2 key-value pairs:
//! //
//! // - "domain" because it was already present in `settings`
//! // - "port" because it was present in defaults
//! let merged = renvy::merge(settings, defaults, None);
//!
//! assert!(merged.get("domain").is_some());
//! assert_eq!(merged.get("domain").unwrap(), &Some(String::from("https://benjaminsattler.net")));
//! assert!(merged.get("port").is_some());
//! assert_eq!(merged.get("port").unwrap(), &Some(String::from("433")));
//! ```
//!
//! ## Cleaning up extra settings
//!
//! You can also remove any key from the user settings that are missing from defaults
//! by passing `Some(true)` to the optional third parameter of [`merge()`]:
//!
//! ```
//! // settings file contains 1 key-value pair
//! let settings = renvy::Settings::from([("domain".into(), Some("https://benjaminsattler.net".into()))]);
//!
//! // defaults file contains 1 other key-value pair
//! let defaults = renvy::Settings::from([("port".into(), Some("433".into()))]);
//!
//! // merging defaults with settings will result in a new object merge::settings
//! // that contains only 1 key-value pair. The key "domain" will be removed because
//! // it does not exist in the defaults:
//! //
//! // - "port" because it was present in defaults
//! let merged = renvy::merge(settings, defaults, Some(true));
//!
//! assert!(merged.get("domain").is_none());
//! assert!(merged.get("port").is_some());
//! assert_eq!(merged.get("port").unwrap(), &Some(String::from("433")));
//! ```
//!
//! ## Writing merged results back to a file
//!
//! The final step is to persist the merged result back into the settings file
//! by invoking [`serialize()`] and [`write_file()`].
//!
//! ```no_run
//! // first we're serializing the object renvy::Settings into a String
//! # let merged = renvy::Settings::from([]);
//! let merged = renvy::serialize(&merged);
//!
//! // then we take that String and write it back into the original settings file
//! let result = renvy::write_file("./.env", &merged);
//! // write_file returns a std::io::Result<()>
//! assert!(result.is_ok());
//! ```

mod io;
mod merge;
pub mod prelude;
mod serde;

pub use io::{read_file, write_file};
pub use merge::{merge, Key, Settings, Value};
pub use serde::{deserialize, serialize};