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
#![doc(html_root_url = "https://docs.rs/curlyconf/0.1.0-alpha.3")] //! ## Curlyconf //! //! Curlyconf is a configuration file reader for the configuration //! file format used by, for example, named.conf and dhcpd.conf. //! //! ## Example config (file.cfg) //! //! ```text //! person charlie { //! fullname "Charlie Brown"; //! address 192.168.1.1; //! } //! person snoopy { //! fullname "Snoopy"; //! } //! ``` //! //! ## Example code //! //! ```no_run //! use serde::Deserialize; //! //! // The initial section of any config is a rust struct. //! #[derive(Debug, Deserialize)] //! struct Config { //! person: Vec<Person>, //! } //! //! #[derive(Debug, Deserialize)] //! struct Person { //! #[serde(rename = "__label__")] //! name: String, //! #[serde(default)] //! fullname: Option<String>, //! #[serde(default)] //! address: Option<std::net::IpAddr>, //! } //! //! fn main() { //! // Read the configuration file. //! let config: Config = match curlyconf::from_file("file.cfg") { //! Ok(cfg) => cfg, //! Err(e) => { //! eprintln!("{}", e); //! std::process::exit(1); //! } //! }; //! //! // Print what we got (println!("{:?}", config) would be easier...). //! for (i, p) in config.person.iter().enumerate() { //! println!("{}: {} fullname {:?} addr {:?}", i, p.name, p.fullname, p.address); //! } //! } //! //! ``` //! //! ## This will print: //! //! ```text //! 0: charlie fullname Some("Charlie Brown") addr Some(V4(192.168.1.1)) //! 1: snoopy fullname Some("Snoopy") addr None //! ``` //! //! Curlyconf uses [serde](https://crates.io/crates/serde) to deserialize the //! configuration file values to rust types, just like almost every other //! crate that does something similar. //! //! ## Sections and values. //! //! The configuration file contains section names, labels, sections, value names, and values: //! //! - **sections**. they have a section\_name, an optional label, and contain //! a list of other sections and values. The rust type of a section is a struct. //! - **values**. this is a value\_name, followed by a value. If the value is a `Vec`, //! there can be multiple values, separated by a comma. //! //! A section can only have a label if: //! //! - it is part of a `HashMap<Key, Section>`, or //! - it is part of a `Vec<Section>` and the rust struct that corresponds to the //! section has a `__label__` field. That field will be set to the label value. //! //! The label type can be any type, it does not have to be a string - it could //! also be, for example, a `PathBuf` or `IpAddr`. //! //! The basic structure of a config file is thus: //! //! ```text //! section_name [label] { //! value_name value [,value...]; //! value_name value [,value...]; //! section_name [label] { //! value_name value [,value...]; //! } //! } //! ``` //! //! `Enum`s are also supported (see the `serde` docs) so you can do things like: //! //! ```no_run //! # use serde::Deserialize; //! #[derive(Debug, Deserialize)] //! struct Config { //! animal: Animal, //! } //! //! #[derive(Debug, Deserialize)] //! enum Animal { //! Cat { //! purrs: bool, //! }, //! Dog { //! barks: bool, //! }, //! } //! ``` //! //! And then have a config like //! //! ```text //! animal cat { //! purrs; //! } //! ``` //! pub(crate) const DEBUG: bool = false; macro_rules! debug { ($($tt:tt)*) => { if $crate::DEBUG { log::debug!($($tt)*) } } } mod cfg; mod de; mod error; mod parser; mod tokenizer; pub use cfg::{from_file, from_str, Builder, Parser, ParserAccess}; pub use error::Error; pub use tokenizer::Mode;