Skip to content

Commit

Permalink
feat(derive): Ability to add chunks
Browse files Browse the repository at this point in the history
  • Loading branch information
andoriyu committed Apr 20, 2020
1 parent 958d82a commit 79631ad
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 20 deletions.
47 changes: 39 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,33 @@

# Uclicious [![Build Status](https://dev.azure.com/andoriyu/personal/_apis/build/status/andoriyu.uclicious?branchName=master)](https://dev.azure.com/andoriyu/personal/_build/latest?definitionId=7&branchName=master) [![codecov](https://codecov.io/gh/andoriyu/uclicious/branch/master/graph/badge.svg)](https://codecov.io/gh/andoriyu/uclicious) [![docs.rs](https://docs.rs/uclicious/badge.svg)](https://docs.rs/uclicious) [![Crates.io](https://img.shields.io/crates/v/uclicious.svg)](https://crates.io/crates/uclicious)

#### Uclicious is a wrapper around Universal Configuration Library (UCL) parser with a lot of sugar.

Uclicious is built on top of [libucl](https://github.com/vstakhov/libucl).
It is much more complex than json or TOML, so I recommend reading documentaiton about it.
Library provides safe, but raw API to that library:
* [What is Uclicious](#what-is-uclicious)
* [Usage](#usage)
+ [Raw API](#raw-api)
+ [Derive-driven](#derive-driven)
- [Validators](#validators)
- [Type Mapping](#type-mapping)
+ [Supported attributes (`#[ucl(..)]`)](#supported-attributes-%23ucl)
- [Structure level](#structure-level)
- [Field level](#field-level)
+ [Additional notes](#additional-notes)
* [Contributing](#contributing)
- [Particular Contributions of Interest](#particular-contributions-of-interest)
* [Goals](#goals)
+ [Not Goals](#not-goals)
* [Special thanks](#special-thanks)
* [LICENSE](#license)
## What is Uclicious

Uclicious is a flexible reduced boilerplate configuration framework.

Uclicious is built on top of [libucl](https://github.com/vstakhov/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();
Expand Down Expand Up @@ -57,7 +79,7 @@ let lookup_result = result.lookup("is_it_good").unwrap();
let maybe: Option<bool> = FromObject::try_from(lookup_result).unwrap();
assert_eq!(Some(true), maybe);
```
### Automatic Derive
### Derive-driven

On top of "raw" interface to libUCL, Uclicious provides an easy way to derive constructor for strucs:
```rust
Expand Down Expand Up @@ -258,6 +280,10 @@ let expected = Mapped {
- a string representation of filepath.
- `expand`
- (optional) if set, then variables would be expanded to absolute.
- `pre_source_hook(...)`
- Optional attribute to run a function before sources are added
- Can be used to register vars handler
- Must take `&mut Parser` as argument and return `Result<(), Into<UclError>>`
- `var(..)`
- Optional attribute to register string variables with the parser.
- Has following nested attributes:
Expand All @@ -269,9 +295,14 @@ let expected = Mapped {
- `include(..)`
- Used to add files into the parser.
- If file doesn't exist or failed to parse, then error will be returned in a constructor.
- Must specify exactly one of following sources: `path`, `chunk` or `chunk_static`
- Has following nested attirbutes:
- (required) `path = string`
- (semi-optional) `path = string`
- File path. Can be absolute or relative to CWD.
- (semi-optional) `chunk = string`
- A string that will be added to parser as a chunk.
- (semi-optional) `chunk_static = string`
- A path to a file that will be included into binary with [`include_str!()`](https://doc.rust-lang.org/std/macro.include_str.html)
- (optional) `priority = u32`
- 0-15 priority for the source. Consult the libUCL documentation for more information.
- (optional) `strategy = uclicious::DuplicateStrategy`
Expand Down Expand Up @@ -321,7 +352,7 @@ PRs, feature requests, bug reports are welcome. I won't be adding CoC — be ci
- Better tests
- Glob support in derive parser section
- Variable handler


## Goals
- Provider safe and convient configuration library
Expand Down
41 changes: 34 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,32 @@
//! # Uclicious [![Build Status](https://dev.azure.com/andoriyu/personal/_apis/build/status/andoriyu.uclicious?branchName=master)](https://dev.azure.com/andoriyu/personal/_build/latest?definitionId=7&branchName=master) [![codecov](https://codecov.io/gh/andoriyu/uclicious/branch/master/graph/badge.svg)](https://codecov.io/gh/andoriyu/uclicious) [![docs.rs](https://docs.rs/uclicious/badge.svg)](https://docs.rs/uclicious) [![Crates.io](https://img.shields.io/crates/v/uclicious.svg)](https://crates.io/crates/uclicious)
//!
//! #### Uclicious is a wrapper around Universal Configuration Library (UCL) parser with a lot of sugar.
//!
//! Uclicious is built on top of [libucl](https://github.com/vstakhov/libucl).
//! It is much more complex than json or TOML, so I recommend reading documentaiton about it.
//! Library provides safe, but raw API to that library:
//! * [What is Uclicious](#what-is-uclicious)
//! * [Usage](#usage)
//! + [Raw API](#raw-api)
//! + [Derive-driven](#derive-driven)
//! - [Validators](#validators)
//! - [Type Mapping](#type-mapping)
//! + [Supported attributes (`#[ucl(..)]`)](#supported-attributes-%23ucl)
//! - [Structure level](#structure-level)
//! - [Field level](#field-level)
//! + [Additional notes](#additional-notes)
//! * [Contributing](#contributing)
//! - [Particular Contributions of Interest](#particular-contributions-of-interest)
//! * [Goals](#goals)
//! + [Not Goals](#not-goals)
//! * [Special thanks](#special-thanks)
//! * [LICENSE](#license)
//! ## What is Uclicious
//!
//! Uclicious is a flexible reduced boilerplate configuration framework.
//!
//! Uclicious is built on top of [libucl](https://github.com/vstakhov/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();
Expand Down Expand Up @@ -55,7 +77,7 @@
//! let maybe: Option<bool> = FromObject::try_from(lookup_result).unwrap();
//! assert_eq!(Some(true), maybe);
//! ```
//! ### Automatic Derive
//! ### Derive-driven
//!
//! On top of "raw" interface to libUCL, Uclicious provides an easy way to derive constructor for strucs:
//! ```rust
Expand Down Expand Up @@ -271,9 +293,14 @@
//! - `include(..)`
//! - Used to add files into the parser.
//! - If file doesn't exist or failed to parse, then error will be returned in a constructor.
//! - Must specify exactly one of following sources: `path`, `chunk` or `chunk_static`
//! - Has following nested attirbutes:
//! - (required) `path = string`
//! - (semi-optional) `path = string`
//! - File path. Can be absolute or relative to CWD.
//! - (semi-optional) `chunk = string`
//! - A string that will be added to parser as a chunk.
//! - (semi-optional) `chunk_static = string`
//! - A path to a file that will be included into binary with [`include_str!()`](https://doc.rust-lang.org/std/macro.include_str.html)
//! - (optional) `priority = u32`
//! - 0-15 priority for the source. Consult the libUCL documentation for more information.
//! - (optional) `strategy = uclicious::DuplicateStrategy`
Expand Down
21 changes: 21 additions & 0 deletions tests/derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,24 @@ fn derive_with_hook() {
assert_eq!("dsa", test.key_one);
assert_eq!("asd", test.key_two);
}

#[test]
fn include_chunk() {
#[derive(Uclicious, Debug)]
#[ucl(include(chunk = r#"key_one = "asd""#))]
struct Test {
key_one: String,
}
let test = Test::builder().unwrap().build().unwrap();
assert_eq!("asd", test.key_one);
}
#[test]
fn include_chunk_with_macro() {
#[derive(Uclicious, Debug)]
#[ucl(include(chunk_static = "fixtures/key_one.ucl"))]
struct Test {
key_one: String,
}
let test = Test::builder().unwrap().build().unwrap();
assert_eq!("asd", test.key_one);
}
1 change: 1 addition & 0 deletions tests/fixtures/key_one.ucl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
key_one = "asd"
30 changes: 25 additions & 5 deletions uclicious_derive/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,12 @@ impl ToTokens for Parser {
}
#[derive(Debug, Clone, FromMeta)]
pub struct Include {
path: String,
#[darling(default)]
path: Option<String>,
#[darling(default)]
chunk: Option<String>,
#[darling(default)]
chunk_static: Option<String>,
#[darling(default)]
priority: Option<u32>,
#[darling(default)]
Expand All @@ -73,16 +78,31 @@ pub struct Include {

impl ToTokens for Include {
fn to_tokens(&self, tokens: &mut TokenStream) {
let path = &self.path;
let priority = self.priority.unwrap_or(0);
let strategy = match self.strategy {
Some(ref s) => s.clone(),
None => bindings::ucl_default_strategy(),
};
let into_trait = bindings::into_trait();
tokens.append_all(quote!(
parser.add_file_full(#path, #into_trait::into(#priority), #strategy)?;
));

match (&self.path, &self.chunk, &self.chunk_static) {
(Some(path), None, None) => {
tokens.append_all(quote!(
parser.add_file_full(#path, #into_trait::into(#priority), #strategy)?;
));
}
(None, Some(chunk), None) => {
tokens.append_all(quote!(
parser.add_chunk_full(#chunk, #into_trait::into(#priority), #strategy)?;
));
}
(None, None, Some(path)) => {
tokens.append_all(quote!(
parser.add_chunk_full(include_str!(#path), #into_trait::into(#priority), #strategy)?;
));
}
(_, _, _) => panic!("Unsupported include combination!"),
}
}
}

Expand Down

0 comments on commit 79631ad

Please sign in to comment.