Uclicious Build Status codecov docs.rs Crates.io

Uclicious is a flexible reduced boilerplate configuration framework.

Uclicious is built on top of libucl. If you ever wrote an nginx configurtion and though "Damn, I wish all configuration files were like this" this is the library for you. Internal parser supports both: nginx-like and json-like formats. JSON parser is a little bit more permissive than - every json file is a valid UCL file, but not other way around. It is much more complex than json or TOML, so I recommend reading documentaiton about it. Author of UCL did a great job documenting it. This library provides both: derive-driven and raw-api driven usage patterns.

Usage

Raw API

Raw API involves interacting with libucl parser via safe api: ```rust use uclicious::*; let mut parser = Parser::default(); let input = r#" teststring = "no scope" afloat = 3.14 aninteger = 69420 isitgood = yes buffersize = 1KB interval = 1s "#; parser.addchunkfull(input, Priority::default(), DEFAULTDUPLICATESTRATEGY).unwrap(); let result = parser.get_object().unwrap();

let lookupresult = result.lookup("teststring").unwrap().asstring().unwrap(); asserteq!(lookupresult.asstr(), "no scope");

let lookupresult = result.lookup("afloat").unwrap().asf64().unwrap(); asserteq!(lookup_result, 3.14f64);

let lookupresult = result.lookup("aninteger").unwrap().asi64().unwrap(); asserteq!(lookup_result, 69420i64);

let lookupresult = result.lookup("isitgood").unwrap().asbool().unwrap(); asserteq!(lookupresult, true);

let lookupresult = result.lookup("buffersize").unwrap().asi64().unwrap(); asserteq!(lookupresult, 1024); let lookupresult = result.lookup("interval").unwrap().astime().unwrap(); asserteq!(lookup_result, 1.0f64); ```

In order to get around rust rules library implemets its own trait FromObject for some basic types: ```rust use uclicious::*; let mut parser = Parser::default(); let input = r#" teststring = "no scope" afloat = 3.14 aninteger = 69420 isitgood = yes buffersize = 1KB "#; parser.addchunkfull(input, Priority::default(), DEFAULTDUPLICATESTRATEGY).unwrap(); let result = parser.get_object().unwrap();

let lookupresult = result.lookup("isitgood").unwrap(); let maybe: Option = FromObject::tryfrom(lookupresult).unwrap(); asserteq!(Some(true), maybe); ```

Derive-driven

On top of "raw" interface to libUCL, Uclicious provides an easy way to derive constructor for strucs: ```rust use uclicious::*; use std::path::PathBuf; use std::net::SocketAddr; use std::collections::HashMap; use std::time::Duration;

[derive(Debug,Uclicious)]

[ucl(var(name = "test", value = "works"))]

struct Connection { #[ucl(default)] enabled: bool, host: String, #[ucl(default = "420")] port: i64, buffer: u64, #[ucl(path = "type")] kind: String, locations: Vec, addr: SocketAddr, extra: Extra, #[ucl(path = "subsection.host")] hosts: Vec, #[ucl(default)] option: Option, gates: HashMap, interval: Duration, }

[derive(Debug,Uclicious)]

[ucl(skip_builder)]

struct Extra { enabled: bool } let mut builder = Connection::builder().unwrap();

let input = r#" enabled = yes host = "some.fake.url" buffer = 1mb type = $test locations = "/etc/" addr = "127.0.0.1:80" extra = { enabled = on } subsection { host = [host1, host2] } interval = 10ms gates { feature1 = on feature2 = off feature_3 = on }"#;

builder.addchunkfull(input, Priority::default(), DEFAULTDUPLICATESTRATEGY).unwrap(); let connection: Connection = builder.build().unwrap(); ```

If you choose to derive builder then ::builder() method will be added to target struct.

Validators

Library supports running optional validators on values before building the resulting struct:

```rust use uclicious::*; mod validators { use uclicious::ObjectError; pub fn ispositive(lookuppath: &str, value: &i64) -> Result<(), ObjectError> { if *value > 0 { Ok(()) } else { Err(ObjectError::other(format!("{} is not a positive number", lookup_path))) } } }

[derive(Debug,Uclicious)]

struct Validated { #[ucl(default, validate="validators::is_positive")] number: i64 } let mut builder = Validated::builder().unwrap();

let input = "number = -1"; builder.addchunkfull(input, Priority::default(), DEFAULTDUPLICATESTRATEGY).unwrap(); assert!(builder.build().is_err()) ```

Type Mapping

If your target structure has types that don't implement FromObject you can use From or TryFrom via intermediate that does:

```rust use uclicious::*; use std::convert::{From,TryFrom};

[derive(Debug, Eq, PartialEq)]

enum Mode { On, Off, }

impl TryFrom for Mode { type Error = ObjectError; fn tryfrom(src: String) -> Result { match src.tolowercase().as_str() { "on" => Ok(Mode::On), "off" => Ok(Mode::Off), _ => Err(ObjectError::other(format!("{} is not supported value", src))) } } }

[derive(Debug, Eq, PartialEq)]

struct WrappedInt(i64);

impl From for WrappedInt { fn from(src: i64) -> WrappedInt { WrappedInt(src) } }

[derive(Debug,Uclicious, Eq, PartialEq)]

struct Mapped { #[ucl(from="i64")] number: WrappedInt, #[ucl(try_from="String")] mode: Mode } let mut builder = Mapped::builder().unwrap();

let input = r#" number = -1, mode = "on" "#; builder.addchunkfull(input, Priority::default(), DEFAULTDUPLICATESTRATEGY).unwrap(); let actual = builder.build().unwrap(); let expected = Mapped { number: WrappedInt(-1), mode: Mode::On }; assert_eq!(expected, actual); ```

Additionally you can provide mapping to your type from ObjectRef: ```rust use uclicious::*;

[derive(Debug, Eq, PartialEq)]

pub enum Mode { On, Off, }

pub fn mapbool(src: ObjectRef) -> Result { let bool: bool = src.tryinto()?; if bool { Ok(Mode::On) } else { Ok(Mode::Off) } }

[derive(Debug,Uclicious, Eq, PartialEq)]

struct Mapped { #[ucl(map="map_bool")] mode: Mode } let mut builder = Mapped::builder().unwrap();

let input = r#" mode = on "#; builder.addchunkfull(input, Priority::default(), DEFAULTDUPLICATESTRATEGY).unwrap(); let actual = builder.build().unwrap(); let expected = Mapped { mode: Mode::On }; ```

Supported attributes (#[ucl(..)])

Structure level

Field level

All field level options are optional.

Additional notes

Contributing

PRs, feature requests, bug reports are welcome. I won't be adding CoC — be civilized.

Particular Contributions of Interest

Goals

Not Goals

Special thanks

LICENSE

BSD-2-Clause.