Skip to content

Commit

Permalink
feat(derive): Ability to add pre_sync_hook
Browse files Browse the repository at this point in the history
  • Loading branch information
andoriyu committed Apr 20, 2020
1 parent 52611de commit 958d82a
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 0 deletions.
4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,10 @@
//! - 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 Down
88 changes: 88 additions & 0 deletions tests/derive.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
use std::ptr::slice_from_raw_parts;
use uclicious::Uclicious;
use uclicious::{variable_handlers, Priority, UclError, DEFAULT_DUPLICATE_STRATEGY};

#[test]
fn derive_with_hook() {
fn add_handlers(parser: &mut uclicious::Parser) -> Result<(), UclError> {
let www = |data: *const ::std::os::raw::c_uchar,
len: usize,
replace: *mut *mut ::std::os::raw::c_uchar,
replace_len: *mut usize,
need_free: *mut bool| {
let var = unsafe {
let slice = slice_from_raw_parts(data, len).as_ref().unwrap();
std::str::from_utf8(slice).unwrap()
};
if var.eq("WWW") {
let test = "asd";
let size = test.as_bytes().len();
unsafe {
*replace = libc::malloc(size).cast();
*replace_len = size;
test.as_bytes()
.as_ptr()
.copy_to_nonoverlapping(*replace, size);
*need_free = true;
}
true
} else {
false
}
};

let zzz = |data: *const ::std::os::raw::c_uchar,
len: usize,
replace: *mut *mut ::std::os::raw::c_uchar,
replace_len: *mut usize,
need_free: *mut bool| {
let var = unsafe {
let slice = slice_from_raw_parts(data, len).as_ref().unwrap();
std::str::from_utf8(slice).unwrap()
};
if var.eq("ZZZ") {
let test = "dsa";
let size = test.as_bytes().len();
unsafe {
*replace = libc::malloc(size).cast();
*replace_len = size;
test.as_bytes()
.as_ptr()
.copy_to_nonoverlapping(*replace, size);
*need_free = true;
}
true
} else {
false
}
};

let mut compound_handler = variable_handlers::compound::CompoundHandler::default();
compound_handler.register_handler(Box::new(www));
compound_handler.register_handler(Box::new(zzz));
parser.set_variables_handler(Box::new(compound_handler));
Ok(())
}

#[derive(Uclicious, Debug)]
#[ucl(pre_source_hook = "add_handlers")]
struct Test {
key_one: String,
key_two: String,
}

let input = r#"
key_one = "${ZZZ}"
key_two = "${WWW}"
"#;

let mut parser = Test::builder().unwrap();
parser
.add_chunk_full(input, Priority::default(), DEFAULT_DUPLICATE_STRATEGY)
.unwrap();

let test = parser.build().unwrap();

assert_eq!("dsa", test.key_one);
assert_eq!("asd", test.key_two);
}
6 changes: 6 additions & 0 deletions uclicious_derive/src/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,13 @@ pub fn clone_trait() -> Path {
pub fn into_trait() -> Path {
syn::parse_str("::std::convert::Into").unwrap()
}
pub fn box_ty() -> Path {
syn::parse_str("::std::boxed::Box").unwrap()
}

pub fn var_handler_trait() -> Path {
syn::parse_str("::uclicious::traits::VariableHandler").unwrap()
}
/// TryInto trait.
pub fn try_into_trait() -> Path {
syn::parse_str("::std::convert::TryInto").unwrap()
Expand Down
9 changes: 9 additions & 0 deletions uclicious_derive/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ pub struct Builder<'a> {
pub includes: Vec<Include>,
pub parser: &'a Parser,
pub vars: Vec<Variable>,
pub pre_source_hook: Option<Path>,
}

impl<'a> Builder<'a> {
Expand Down Expand Up @@ -198,6 +199,13 @@ impl<'a> ToTokens for Builder<'a> {
let result_ty = bindings::result_ty();
let ucl_error_ty = bindings::ucl_parser_error();
let parser = self.parser;
let pre_source_hook = if let Some(ref hook) = self.pre_source_hook {
quote! {
#hook(&mut parser)?;
}
} else {
quote!()
};
tokens.append_all(quote!(
#[derive(#derived_traits)]
#builder_doc_comment
Expand All @@ -212,6 +220,7 @@ impl<'a> ToTokens for Builder<'a> {
#builder_vis fn new() -> #result_ty<Self #ty_generics #where_clause, #ucl_error_ty> {
#parser
#(#vars)*
#pre_source_hook
#(#includes)*
Ok(
Self {
Expand Down
4 changes: 4 additions & 0 deletions uclicious_derive/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,9 @@ pub struct Options {

#[darling(default, multiple, rename = "var")]
vars: Vec<Variable>,

#[darling(default)]
pre_source_hook: Option<Path>,
}

/// Data extracted from the fields of the input struct.
Expand Down Expand Up @@ -372,6 +375,7 @@ impl Options {
includes: self.include.clone(),
parser: &self.parser,
vars: self.vars.clone(),
pre_source_hook: self.pre_source_hook.clone(),
}
}
pub fn as_build_method(&self) -> BuildMethod {
Expand Down
11 changes: 11 additions & 0 deletions uclicious_derive/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ impl ToTokens for ParserMethods {
let result = bindings::result_ty();
let err = bindings::ucl_parser_error();
let path = bindings::path_ty();
let box_ty = bindings::box_ty();
let var_handler_trait = bindings::var_handler_trait();
tokens.append_all(quote! (
/// Add a chunk of text to the parser. String must:
/// - not have `\0` character;
Expand All @@ -60,6 +62,15 @@ impl ToTokens for ParserMethods {
self.__parser.register_variable(var, value);
self
}
/// A safe counterpart of [`Parser::set_variable_handler_raw`](#method.set_variables_handler_raw). Unlike unsafe version this one takes ownership of a handler and ensures it stays alive as long as parser does.
///
/// ### Caveats
///
/// Parser can have only bar handler. In order to have multiple, please use [`CompoundHandler`](../../variable_handlers/compound/struct.CompoundHandler.html) to join multiple handlers into one.
#vis fn set_variables_handler(&mut self, handler: #box_ty<dyn #var_handler_trait>) -> &mut Self {
self.__parser.set_variables_handler(handler);
self
}
/// Add the standard file variables to the `parser` based on the `filename` specified:
///
/// - `$FILENAME`- a filename of ucl input
Expand Down

0 comments on commit 958d82a

Please sign in to comment.