Add JSON support for exprs (fixes #146)
This commit is contained in:
parent
72457aaa11
commit
ce4c22e2a6
5 changed files with 97 additions and 4 deletions
18
Cargo.lock
generated
18
Cargo.lock
generated
|
@ -326,6 +326,7 @@ dependencies = [
|
|||
"regex",
|
||||
"roxmltree",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"simple-signal",
|
||||
"smart-default",
|
||||
"structopt",
|
||||
|
@ -830,6 +831,12 @@ dependencies = [
|
|||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
|
||||
|
||||
[[package]]
|
||||
name = "lasso"
|
||||
version = "0.3.1"
|
||||
|
@ -1424,6 +1431,17 @@ dependencies = [
|
|||
"syn 1.0.64",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.3.0"
|
||||
|
|
|
@ -33,6 +33,7 @@ derive_more = "0.99"
|
|||
maplit = "1"
|
||||
structopt = "0.3"
|
||||
serde = {version = "1.0", features = ["derive"]}
|
||||
serde_json = "1.0"
|
||||
extend = "1"
|
||||
grass = "0.10"
|
||||
num = "0.4"
|
||||
|
|
|
@ -58,6 +58,7 @@ pub enum AttrValueExpr {
|
|||
BinOp(Box<AttrValueExpr>, BinOp, Box<AttrValueExpr>),
|
||||
UnaryOp(UnaryOp, Box<AttrValueExpr>),
|
||||
IfElse(Box<AttrValueExpr>, Box<AttrValueExpr>, Box<AttrValueExpr>),
|
||||
JsonAccessIndex(Box<AttrValueExpr>, Box<AttrValueExpr>),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for AttrValueExpr {
|
||||
|
@ -68,6 +69,7 @@ impl std::fmt::Display for AttrValueExpr {
|
|||
AttrValueExpr::BinOp(l, op, r) => write!(f, "({} {} {})", l, op, r),
|
||||
AttrValueExpr::UnaryOp(op, x) => write!(f, "{}{}", op, x),
|
||||
AttrValueExpr::IfElse(a, b, c) => write!(f, "(if {} then {} else {})", a, b, c),
|
||||
AttrValueExpr::JsonAccessIndex(value, index) => write!(f, "{}[{}]", value, index),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -101,6 +103,7 @@ impl AttrValueExpr {
|
|||
IfElse(box a, box b, box c) => {
|
||||
Ok(IfElse(box a.resolve_refs(variables)?, box b.resolve_refs(variables)?, box c.resolve_refs(variables)?))
|
||||
}
|
||||
JsonAccessIndex(box a, box b) => Ok(JsonAccessIndex(box a.resolve_refs(variables)?, box b.resolve_refs(variables)?)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,6 +124,11 @@ impl AttrValueExpr {
|
|||
refs.append(&mut c.var_refs());
|
||||
refs
|
||||
}
|
||||
JsonAccessIndex(box a, box b) => {
|
||||
let mut refs = a.var_refs();
|
||||
refs.append(&mut b.var_refs());
|
||||
refs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -163,6 +171,25 @@ impl AttrValueExpr {
|
|||
no.eval(values)
|
||||
}
|
||||
}
|
||||
AttrValueExpr::JsonAccessIndex(val, index) => {
|
||||
let val = val.eval(values)?;
|
||||
let index = index.eval(values)?;
|
||||
match val.as_json_value()? {
|
||||
serde_json::Value::Array(val) => {
|
||||
let index = index.as_i32()?;
|
||||
let indexed_value = val.get(index as usize).unwrap_or(&serde_json::Value::Null);
|
||||
Ok(PrimitiveValue::from(indexed_value))
|
||||
}
|
||||
serde_json::Value::Object(val) => {
|
||||
let indexed_value = val
|
||||
.get(&index.as_string()?)
|
||||
.or_else(|| val.get(&format!("{}", index.as_i32().ok()?)))
|
||||
.unwrap_or(&serde_json::Value::Null);
|
||||
Ok(PrimitiveValue::from(indexed_value))
|
||||
}
|
||||
_ => bail!("Unable to index into value {}", val),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -70,12 +70,22 @@ fn parse_factor(i: &str) -> IResult<&str, AttrValueExpr, VerboseError<&str>> {
|
|||
))
|
||||
}
|
||||
|
||||
fn parse_term3(i: &str) -> IResult<&str, AttrValueExpr, VerboseError<&str>> {
|
||||
fn parse_object_index(i: &str) -> IResult<&str, AttrValueExpr, VerboseError<&str>> {
|
||||
let (i, initial) = parse_factor(i)?;
|
||||
let (i, remainder) = many0(alt((
|
||||
map(preceded(tag("*"), parse_factor), |x| (BinOp::Times, x)),
|
||||
map(preceded(tag("/"), parse_factor), |x| (BinOp::Div, x)),
|
||||
map(preceded(tag("%"), parse_factor), |x| (BinOp::Mod, x)),
|
||||
delimited(tag("["), ws(parse_expr), tag("]")),
|
||||
map(preceded(tag("."), parse_identifier), |x| AttrValueExpr::Literal(AttrValue::from_primitive(x))),
|
||||
)))(i)?;
|
||||
let indexes = remainder.into_iter().fold(initial, |acc, index| AttrValueExpr::JsonAccessIndex(box acc, box index));
|
||||
Ok((i, indexes))
|
||||
}
|
||||
|
||||
fn parse_term3(i: &str) -> IResult<&str, AttrValueExpr, VerboseError<&str>> {
|
||||
let (i, initial) = parse_object_index(i)?;
|
||||
let (i, remainder) = many0(alt((
|
||||
map(preceded(tag("*"), parse_object_index), |x| (BinOp::Times, x)),
|
||||
map(preceded(tag("/"), parse_object_index), |x| (BinOp::Div, x)),
|
||||
map(preceded(tag("%"), parse_object_index), |x| (BinOp::Mod, x)),
|
||||
)))(i)?;
|
||||
|
||||
let exprs = remainder.into_iter().fold(initial, |acc, (op, expr)| AttrValueExpr::BinOp(box acc, op, box expr));
|
||||
|
@ -172,6 +182,27 @@ mod test {
|
|||
),
|
||||
AttrValueExpr::parse("if a then b else c").unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
AttrValueExpr::JsonAccessIndex(
|
||||
box AttrValueExpr::VarRef(VarName("array".to_string())),
|
||||
box AttrValueExpr::BinOp(
|
||||
box AttrValueExpr::Literal(AttrValue::from_primitive("1")),
|
||||
BinOp::Plus,
|
||||
box AttrValueExpr::Literal(AttrValue::from_primitive("2"))
|
||||
)
|
||||
),
|
||||
AttrValueExpr::parse(r#"(array)[1+2]"#).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
AttrValueExpr::JsonAccessIndex(
|
||||
box AttrValueExpr::JsonAccessIndex(
|
||||
box AttrValueExpr::VarRef(VarName("object".to_string())),
|
||||
box AttrValueExpr::Literal(AttrValue::from_primitive("field".to_string())),
|
||||
),
|
||||
box AttrValueExpr::Literal(AttrValue::from_primitive("field2".to_string())),
|
||||
),
|
||||
AttrValueExpr::parse(r#"object.field.field2"#).unwrap()
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn test_complex() {
|
||||
|
|
|
@ -77,6 +77,17 @@ impl From<&str> for PrimitiveValue {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<&serde_json::Value> for PrimitiveValue {
|
||||
fn from(v: &serde_json::Value) -> Self {
|
||||
PrimitiveValue(
|
||||
v.as_str()
|
||||
.map(|x| x.to_string())
|
||||
.or_else(|| serde_json::to_string(v).ok())
|
||||
.unwrap_or_else(|| "<invalid json value>".to_string()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl PrimitiveValue {
|
||||
pub fn from_string(s: String) -> Self {
|
||||
PrimitiveValue(s)
|
||||
|
@ -106,6 +117,11 @@ impl PrimitiveValue {
|
|||
pub fn as_vec(&self) -> Result<Vec<String>> {
|
||||
parse_vec(self.0.to_owned()).map_err(|e| anyhow!("Couldn't convert {:#?} to a vec: {}", &self, e))
|
||||
}
|
||||
|
||||
pub fn as_json_value(&self) -> Result<serde_json::Value> {
|
||||
serde_json::from_str::<serde_json::Value>(&self.0)
|
||||
.with_context(|| format!("Couldn't convert {:#?} to a json object", &self))
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_vec(a: String) -> Result<Vec<String>> {
|
||||
|
|
Loading…
Add table
Reference in a new issue