renvy/
lib.rs

1#![warn(missing_docs)]
2#![deny(rustdoc::broken_intra_doc_links)]
3
4//! This crate provides easy-to-use functionality for managing settings files
5//! that are based on templates (e.g. `.env` and `.env.dist` files).
6//!
7//! This crate assumes that the files it manages are `key=value` pairs which it
8//! understands. It will add keys to the settings if they exist in the template file
9//! and optionally it'll remove keys from the settings file if they are absent from
10//! the template file.
11//!
12//! ## Installation
13//!
14//! ```sh
15//! cargo install librenvy
16//! ```
17//!
18//! ## Parsing files
19//!
20//! This example shows how you can easily read existing files into [`Settings`] struct's
21//! with the function [`deserialize()`].
22//! [`Settings`] are the foundation for any further processing by this crate.
23//!
24//! ```no_run
25//! // read_file returns a std::io::Result<String>
26//! let settings = renvy::read_file("./.env");
27//! assert!(settings.is_ok());
28//! if let Ok(settings) = settings {
29//!     // renvy::deserialize consumes this String and returns an instance of renvy::Settings
30//!     let settings: renvy::Settings = renvy::deserialize(&settings);
31//!     println!("Number of settings read: {}", &settings.len());
32//!     settings.iter().for_each(|(key, value)| {
33//!         println!("{:?}: {:?}\n", key, value);
34//!     });
35//! } else {
36//!     println!("Unable to read settings file!");
37//! }
38//!
39//! // you can use the same function for reading settings and template files
40//! let defaults = renvy::read_file("./.env.dist");
41//! assert!(defaults.is_ok());
42//! if let Ok(defaults) = defaults {
43//!     // we're reusing the same data structure for defaults
44//!     let defaults: renvy::Settings = renvy::deserialize(&defaults);
45//!     println!("Number of defaults read: {}", &defaults.len());
46//!     defaults.iter().for_each(|(key, value)| {
47//!         println!("{:?}: {:?}\n", key, value);
48//!     });
49//! } else {
50//!     println!("Unable to read defaults file!");
51//! }
52//! ```
53//!
54//! ## Updating settings based on template
55//!
56//! [`merge()`] allows you to update settings based on an existing template.
57//! New keys in the template will be added to the settings with the default
58//! value given in the template.
59//!
60//! ```
61//! // settings file contains 1 key-value pair
62//! let settings = renvy::Settings::from([("domain".into(), Some("https://benjaminsattler.net".into()))]);
63//!
64//! // defaults file contains 1 other key-value pair
65//! let defaults = renvy::Settings::from([("port".into(), Some("433".into()))]);
66//!
67//! // merging defaults with settings will result in a new object merge::settings
68//! // that contains 2 key-value pairs:
69//! //
70//! // - "domain" because it was already present in `settings`
71//! // - "port" because it was present in defaults
72//! let merged = renvy::merge(settings, defaults, None);
73//!
74//! assert!(merged.get("domain").is_some());
75//! assert_eq!(merged.get("domain").unwrap(), &Some(String::from("https://benjaminsattler.net")));
76//! assert!(merged.get("port").is_some());
77//! assert_eq!(merged.get("port").unwrap(), &Some(String::from("433")));
78//! ```
79//!
80//! ## Cleaning up extra settings
81//!
82//! You can also remove any key from the user settings that are missing from defaults
83//! by passing `Some(true)` to the optional third parameter of [`merge()`]:
84//!
85//! ```
86//! // settings file contains 1 key-value pair
87//! let settings = renvy::Settings::from([("domain".into(), Some("https://benjaminsattler.net".into()))]);
88//!
89//! // defaults file contains 1 other key-value pair
90//! let defaults = renvy::Settings::from([("port".into(), Some("433".into()))]);
91//!
92//! // merging defaults with settings will result in a new object merge::settings
93//! // that contains only 1 key-value pair. The key "domain" will be removed because
94//! // it does not exist in the defaults:
95//! //
96//! // - "port" because it was present in defaults
97//! let merged = renvy::merge(settings, defaults, Some(true));
98//!
99//! assert!(merged.get("domain").is_none());
100//! assert!(merged.get("port").is_some());
101//! assert_eq!(merged.get("port").unwrap(), &Some(String::from("433")));
102//! ```
103//!
104//! ## Writing merged results back to a file
105//!
106//! The final step is to persist the merged result back into the settings file
107//! by invoking [`serialize()`] and [`write_file()`].
108//!
109//! ```no_run
110//! // first we're serializing the object renvy::Settings into a String
111//! # let merged = renvy::Settings::from([]);
112//! let merged = renvy::serialize(&merged);
113//!
114//! // then we take that String and write it back into the original settings file
115//! let result = renvy::write_file("./.env", &merged);
116//! // write_file returns a std::io::Result<()>
117//! assert!(result.is_ok());
118//! ```
119
120mod io;
121mod merge;
122pub mod prelude;
123mod serde;
124
125pub use io::{read_file, write_file};
126pub use merge::{merge, Key, Settings, Value};
127pub use serde::{deserialize, serialize};