diff --git a/src/context.rs b/src/context.rs index c293b5a..8814f77 100644 --- a/src/context.rs +++ b/src/context.rs @@ -12,6 +12,15 @@ pub struct Path; #[derive(Debug)] pub struct PathResult(pub PathOutput); +#[derive(Debug, PartialEq)] +pub enum FormatOp { + FormatString { + format: String, + arguments: Vec, + alias: String, + }, +} + #[derive(Debug, PartialEq)] pub enum PickFilterInner { None, @@ -28,6 +37,7 @@ pub enum Filter { Child(String), Descendant(String), Pick(Vec), + Format(FormatOp), ArrayIndex(usize), ArrayFrom(usize), ArrayTo(usize), @@ -53,7 +63,7 @@ trait KeyFormater { struct FormatImpl<'a> { format: &'a str, value: &'a Value, - keys: &'a [&'a str], + keys: &'a Vec, } impl<'a> FormatImpl<'a> { @@ -265,6 +275,78 @@ impl<'a> Context<'a> { self.stack.clone(), )), + (Filter::Format(ref target_format), Some(tail)) => match *current.value { + Value::Object(ref obj) => { + let FormatOp::FormatString { + format: ref fmt, + arguments: ref args, + alias: ref alias, + } = target_format; + + let output: Option = FormatImpl { + format: fmt, + value: ¤t.value, + keys: args, + } + .format(); + + let mut result = obj.clone(); + result.insert(alias.to_string(), Value::String(output.unwrap())); + + let tlen = tail.len(); + if tlen == 0 { + self.results + .borrow_mut() + .push(Rc::new(Value::Object(result))); + } else { + self.stack.borrow_mut().push(StackItem::new( + Rc::new(Value::Object(result)), + tail, + self.stack.clone(), + )); + } + } + Value::Array(ref array) => { + for e in array.iter() { + let FormatOp::FormatString { + format: ref fmt, + arguments: ref args, + alias: ref alias, + } = target_format; + + let output: Option = FormatImpl { + format: fmt, + value: ¤t.value, + keys: args, + } + .format(); + + let mut result = e.clone(); + match result.as_object_mut() { + Some(ref mut handle) => { + handle.insert( + alias.to_string(), + Value::String(output.unwrap()), + ); + } + _ => {} + }; + + let tlen = tail.len(); + if tlen == 0 { + self.results.borrow_mut().push(Rc::new(result)); + } else { + self.stack.borrow_mut().push(StackItem::new( + Rc::new(result), + tail, + self.stack.clone(), + )); + } + } + } + _ => {} + }, + (Filter::ArrayIndex(ref index), Some(tail)) => match *current.value { Value::Array(ref array) => { if *index < array.len() { @@ -649,7 +731,7 @@ mod test { "alias": "jetro" }); - let keys = vec!["name", "alias"]; + let keys = vec!["name".to_string(), "alias".to_string()]; let format_impl: Box = Box::new(FormatImpl { format: "{}_{}", diff --git a/src/grammar.pest b/src/grammar.pest index bf6329a..eae0555 100644 --- a/src/grammar.pest +++ b/src/grammar.pest @@ -25,7 +25,7 @@ pick = { "pick" ~ (" ")* ~ "(" ~ (literal_keyed | sub_expression_keyed ) ~ (("," squash = { "squash" ~ "(" ~ ")" } squashFn = { slash ~ squash } pickFn = { slash ~ pick } -formats = { "formats" ~ whitespc ~ lparen ~ literal ~ ((",")* ~ whitespc ~ literal)+ ~ whitespc ~ rparen} +formats = { "formats" ~ whitespc ~ lparen ~ literal ~ ((",")* ~ whitespc ~ literal)+ ~ whitespc ~ rparen ~ whitespc ~ as} formatsFn = { slash ~ formats } sub_expression = { path ~ (pickFn | child | any_child | descendant_child | array_index | slice | array_to | array_from)* } diff --git a/src/parser.rs b/src/parser.rs index 3e4cc13..e46f2b7 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,6 +1,6 @@ use pest_derive::Parser as Parse; -use crate::context::{Filter, PickFilterInner}; +use crate::context::{Filter, FormatOp, PickFilterInner}; use pest::Parser; #[derive(Parse)] @@ -14,6 +14,45 @@ pub fn parse<'a>(input: &'a str) -> Vec { for token in root.into_inner() { match token.as_rule() { Rule::path => actions.push(Filter::Root), + Rule::formatsFn => { + let mut arguments: Vec = vec![]; + let mut elem = token.into_inner().nth(1).unwrap().into_inner(); + let head = elem.next().unwrap().as_str(); + let format: String = { + if head.len() > 2 { + head[1..head.len() - 1].to_string() + } else { + "".to_string() + } + }; + let mut alias: String = "unknown".to_string(); + for e in elem { + match e.as_rule() { + Rule::literal => { + let name: String = { + let elem = e.as_str(); + if elem.len() > 2 { + elem[1..elem.len() - 1].to_string() + } else { + "".to_string() + } + }; + arguments.push(name); + } + _ => { + let e = e.as_str(); + if e.len() > 2 { + alias = e[4..e.len() - 1].to_string(); + } + } + } + } + actions.push(Filter::Format(FormatOp::FormatString { + format, + arguments, + alias, + })); + } Rule::any_child => actions.push(Filter::AnyChild), Rule::array_index => { let elem = token.into_inner().nth(1).unwrap().as_span(); @@ -241,4 +280,20 @@ mod test { let actions = parse(">/[1:4]"); assert_eq!(actions, vec![Filter::Root, Filter::Slice(1, 4),]); } + + #[test] + fn test_format() { + let actions = parse(">/formats('{}{}', 'name', 'alias') as 'some_key'"); + assert_eq!( + actions, + vec![ + Filter::Root, + Filter::Format(FormatOp::FormatString { + format: "{}{}".to_string(), + arguments: vec!["name".to_string(), "alias".to_string()], + alias: "some_key".to_string(), + }) + ] + ); + } }