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
//! Hierarchical INI parser and tree structure.
//!
//! ```
//! use hini::tree::build::{ToHiniString, TreeBuilder};
//! use hini::tree::Node;
//! use std::rc::Rc;
//!
//! let config_contents = "
//! ; This is an example file in hini format. comments start with # or ; and go
//! ; to the end of the line. Files are UTF-8 Unicode text.
//!
//! [ section ] ; Subsequent assignments become children of this node.
//! key name = a value ; Assignments give a key name and its value. Names and
//! ; values are (valid UTF-8) strings.
//!
//! [[ sub-section ]] # This section becomes a child of the top section node.
//! another-key = another value # These keys are children of the sub-section.
//! ";
//!
//! let tree_rc_ref = TreeBuilder::parse(config_contents, None).build_new().unwrap();
//! let mut config_root = Rc::into_inner(tree_rc_ref).unwrap().into_inner();
//!
//! let section = config_root.get_child("section");
//! let value = section.borrow().get_child_value("key name");
//! assert_eq!(value, Some(String::from("a value")));
//!
//! let subsection = section.borrow_mut().get_child("sub-section");
//! let value = subsection.borrow().get_child_value("another-key");
//! assert_eq!(value, Some(String::from("another value")));
//!
//! let contents = ToHiniString {
//! indent: "",
//! section_separator: "",
//! start_comment: "",
//! ..Default::default()
//! }.build(&config_root);
//! assert_eq!(contents, String::from("[section]
//! key name = a value
//! [[sub-section]]
//! another-key = another value
//! "));
//! ```
pub mod parsers;
pub mod tree;
#[cfg(test)]
mod tests {
use crate::tree::build::{ToHiniString, TreeBuilder};
use std::rc::Rc;
#[test]
#[allow(non_snake_case)]
fn test_README() {
let contents = String::from(r#"
; This is an example hini document. Comments begin with ; or # and go to the
; end of the line.
[serve]
listen = 0.0.0.0:80
docroot = /srv/www/htdocs
banner = "\u{1F308} made up server \u{1F308}" ; "\u{1F308}" -> 🌈
[[methods]]
GET = 🗸
POST = 🗸
PUT = 🚫
[[redirects]]
/restricted = "/no.html#restricted"
[daemon]
control = unix:///var/run/webserver.sock
"#);
//use hini::tree::build::{ToHiniString, TreeBuilder};
//use std::fs;
//use std::rc::Rc;
//let contents = fs::read_to_string("/etc/webserver.hini")?;
let root = TreeBuilder::parse(&contents, None).build_new().unwrap();
let serve = root.borrow_mut().get_child("serve");
let listen = serve.borrow().get_child_value("listen");
let docroot = serve.borrow().get_child_value("docroot");
let banner = serve.borrow().get_child_value("banner");
assert_eq!(listen, Some(String::from("0.0.0.0:80")));
assert_eq!(docroot, Some(String::from("/srv/www/htdocs")));
assert_eq!(banner, Some(String::from("\u{1F308} made up server \u{1F308}")));
// You can access nodes' comments from code too:
assert_eq!(serve.borrow_mut().get_child("banner").borrow().comment(),
Some(r#""\u{1F308}" -> 🌈"#));
let methods = serve.borrow_mut().get_child("methods");
// Note: everything is a string in hini, so bools require manual handling.
let get = methods.borrow().get_child_value("GET").unwrap() == "\u{1F5F8}";
let post = methods.borrow().get_child_value("POST").unwrap() == "\u{1F5F8}";
let put = methods.borrow().get_child_value("PUT").unwrap() == "\u{1F5F8}";
assert_eq!(get, true);
assert_eq!(post, true);
assert_eq!(put, false);
let redirects = serve.borrow_mut().get_child("redirects");
for r in redirects.borrow().children() {
// For example...
assert_eq!(r.borrow().name(), "/restricted");
assert_eq!(r.borrow().value(), Some("/no.html#restricted"));
}
let daemon = root.borrow_mut().get_child("daemon");
let control = daemon.borrow().get_child_value("control");
assert_eq!(control, Some(String::from("unix:///var/run/webserver.sock")));
// ----
// Now let's override some options as if a user wanted to stand up their own
// local instance of our web server.
serve.borrow_mut().set_child_value("listen", Some("127.0.0.1:8080"));
serve.borrow_mut().set_child_value("docroot", Some("/home/user/public"));
methods.borrow_mut().set_child_value("POST", Some("🚫"));
daemon.borrow_mut().set_child_value("control", Some("")); // Present but empty
let override_contents = ToHiniString {
dirty_only: true,
..Default::default()
}.build(&root.borrow());
assert_eq!(override_contents, String::from(r#"[serve]
listen = 127.0.0.1:8080
docroot = /home/user/public
[[methods]]
POST = 🚫
[daemon]
control = ""
"#));
//fs::write("/home/user/.config/webserver.hini", override_contents);
// ----
// Now to read the global + user configs on webserver startup.
//let contents = fs::read_to_string("/etc/webserver.hini")?;
//let override_contents = fs::read_to_string(
// "/home/user/.config/webserver.hini")?;
let root = TreeBuilder::parse(&contents, None).build_new().unwrap();
TreeBuilder::parse(&override_contents, None).build_over(Rc::clone(&root)).unwrap();
// The tree is now back in the state it was in when we wrote it to disk above.
// Non-node comments and unnecessary quotes are gone when we build it back to a
// string.
let contents = ToHiniString::default().build(&root.borrow());
assert_eq!(contents, String::from(r#"[serve]
listen = 127.0.0.1:8080
docroot = /home/user/public
banner = 🌈 made up server 🌈 # "\u{1F308}" -> 🌈
[[methods]]
GET = 🗸
POST = 🚫
PUT = 🚫
[[redirects]]
/restricted = "/no.html#restricted"
[daemon]
control = ""
"#));
}
}