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",
|
"regex",
|
||||||
"roxmltree",
|
"roxmltree",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"simple-signal",
|
"simple-signal",
|
||||||
"smart-default",
|
"smart-default",
|
||||||
"structopt",
|
"structopt",
|
||||||
|
@ -830,6 +831,12 @@ dependencies = [
|
||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "0.4.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lasso"
|
name = "lasso"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
|
@ -1424,6 +1431,17 @@ dependencies = [
|
||||||
"syn 1.0.64",
|
"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]]
|
[[package]]
|
||||||
name = "signal-hook-registry"
|
name = "signal-hook-registry"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
|
|
|
@ -33,6 +33,7 @@ derive_more = "0.99"
|
||||||
maplit = "1"
|
maplit = "1"
|
||||||
structopt = "0.3"
|
structopt = "0.3"
|
||||||
serde = {version = "1.0", features = ["derive"]}
|
serde = {version = "1.0", features = ["derive"]}
|
||||||
|
serde_json = "1.0"
|
||||||
extend = "1"
|
extend = "1"
|
||||||
grass = "0.10"
|
grass = "0.10"
|
||||||
num = "0.4"
|
num = "0.4"
|
||||||
|
|
|
@ -58,6 +58,7 @@ pub enum AttrValueExpr {
|
||||||
BinOp(Box<AttrValueExpr>, BinOp, Box<AttrValueExpr>),
|
BinOp(Box<AttrValueExpr>, BinOp, Box<AttrValueExpr>),
|
||||||
UnaryOp(UnaryOp, Box<AttrValueExpr>),
|
UnaryOp(UnaryOp, Box<AttrValueExpr>),
|
||||||
IfElse(Box<AttrValueExpr>, Box<AttrValueExpr>, Box<AttrValueExpr>),
|
IfElse(Box<AttrValueExpr>, Box<AttrValueExpr>, Box<AttrValueExpr>),
|
||||||
|
JsonAccessIndex(Box<AttrValueExpr>, Box<AttrValueExpr>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for 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::BinOp(l, op, r) => write!(f, "({} {} {})", l, op, r),
|
||||||
AttrValueExpr::UnaryOp(op, x) => write!(f, "{}{}", op, x),
|
AttrValueExpr::UnaryOp(op, x) => write!(f, "{}{}", op, x),
|
||||||
AttrValueExpr::IfElse(a, b, c) => write!(f, "(if {} then {} else {})", a, b, c),
|
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) => {
|
IfElse(box a, box b, box c) => {
|
||||||
Ok(IfElse(box a.resolve_refs(variables)?, box b.resolve_refs(variables)?, box c.resolve_refs(variables)?))
|
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.append(&mut c.var_refs());
|
||||||
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)
|
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, initial) = parse_factor(i)?;
|
||||||
let (i, remainder) = many0(alt((
|
let (i, remainder) = many0(alt((
|
||||||
map(preceded(tag("*"), parse_factor), |x| (BinOp::Times, x)),
|
delimited(tag("["), ws(parse_expr), tag("]")),
|
||||||
map(preceded(tag("/"), parse_factor), |x| (BinOp::Div, x)),
|
map(preceded(tag("."), parse_identifier), |x| AttrValueExpr::Literal(AttrValue::from_primitive(x))),
|
||||||
map(preceded(tag("%"), parse_factor), |x| (BinOp::Mod, 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)?;
|
)))(i)?;
|
||||||
|
|
||||||
let exprs = remainder.into_iter().fold(initial, |acc, (op, expr)| AttrValueExpr::BinOp(box acc, op, box expr));
|
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()
|
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]
|
#[test]
|
||||||
fn test_complex() {
|
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 {
|
impl PrimitiveValue {
|
||||||
pub fn from_string(s: String) -> Self {
|
pub fn from_string(s: String) -> Self {
|
||||||
PrimitiveValue(s)
|
PrimitiveValue(s)
|
||||||
|
@ -106,6 +117,11 @@ impl PrimitiveValue {
|
||||||
pub fn as_vec(&self) -> Result<Vec<String>> {
|
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))
|
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>> {
|
fn parse_vec(a: String) -> Result<Vec<String>> {
|
||||||
|
|
Loading…
Add table
Reference in a new issue