More builtins and semantic improvements (#66)

- base64 builtins
- base64url builtins
- jsonschema builtins
- json.remove builtin
- handle composite index variables
- use dashu_float since rust_decimal has lesser precision

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
This commit is contained in:
Anand Krishnamoorthi
2023-12-21 13:34:48 -08:00
committed by GitHub
parent 577e1aa8db
commit f6140b6be5
12 changed files with 668 additions and 153 deletions

View File

@@ -15,7 +15,7 @@ use anyhow::{bail, Result};
pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
m.insert("json.filter", (json_filter, 2));
// m.insert("json.patch", (json_patch));
m.insert("json.remove", (json_remove, 2));
m.insert("object.filter", (filter, 2));
m.insert("object.get", (get, 3));
m.insert("object.keys", (keys, 1));
@@ -25,6 +25,9 @@ pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
fn json_filter_impl(v: &Value, filter: &Value) -> Value {
let filters = match filter {
Value::Object(fields) if fields.len() == 1 && filter[&Value::Null] == Value::Null => {
return v.clone()
}
Value::Object(fields) if !fields.is_empty() => fields,
_ => return v.clone(),
};
@@ -76,6 +79,69 @@ fn json_filter_impl(v: &Value, filter: &Value) -> Value {
}
}
fn json_remove_impl(v: &Value, filter: &Value) -> Value {
let filters = match filter {
Value::Object(fields) if !fields.is_empty() => fields,
_ => return v.clone(),
};
if filter[&Value::Null] == Value::Null {
return Value::Undefined;
}
match v {
Value::Array(a) => {
let mut items = vec![];
for (idx, item) in a.iter().enumerate() {
let idx = Value::String(format!("{idx}").into());
if let Some(f) = filters.get(&idx) {
let v = json_remove_impl(item, f);
if v != Value::Undefined {
items.push(v);
}
} else {
// Retain the item.
items.push(item.clone());
}
}
Value::from_array(items)
}
Value::Set(s) => {
let mut items = BTreeSet::new();
for item in s.iter() {
if let Some(f) = filters.get(item) {
let v = json_remove_impl(item, f);
if v != Value::Undefined {
items.insert(v);
}
} else {
// Retain the item.
items.insert(item.clone());
}
}
Value::from_set(items)
}
Value::Object(obj) => {
let mut items = BTreeMap::new();
for (key, value) in obj.iter() {
if let Some(f) = filters.get(key) {
let v = json_remove_impl(value, f);
if v != Value::Undefined {
items.insert(key.clone(), v);
}
} else {
items.insert(key.clone(), value.clone());
}
}
Value::from_map(items)
}
_ => Value::Undefined,
}
}
fn merge_filters(
name: &str,
param: &Expr,
@@ -94,6 +160,9 @@ fn merge_filters(
}
f = vref;
}
if let Ok(f) = f.as_object_mut() {
f.insert(Value::Null, Value::Null);
};
filters = fc;
}
Some(Value::Array(a)) => {
@@ -109,6 +178,9 @@ fn merge_filters(
};
f = vref;
}
if let Ok(f) = f.as_object_mut() {
f.insert(Value::Null, Value::Null);
};
filters = fc;
}
Some(_) => {
@@ -119,6 +191,7 @@ fn merge_filters(
None => break,
}
}
Ok(filters)
}
@@ -133,9 +206,29 @@ fn json_filter(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool)
_ => bail!(span.error(format!("`{name}` requires set/array argument").as_str())),
};
if let Ok(v) = filters.as_object() {
if v.is_empty() {
return Ok(Value::new_object());
}
}
Ok(json_filter_impl(&args[0], &filters))
}
fn json_remove(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "json.remove";
ensure_args_count(span, name, params, args, 2)?;
ensure_object(name, &params[0], args[0].clone())?;
let filters = match &args[1] {
Value::Array(a) => merge_filters(name, &params[1], &mut a.iter(), Value::new_object())?,
Value::Set(s) => merge_filters(name, &params[1], &mut s.iter(), Value::new_object())?,
_ => bail!(span.error(format!("`{name}` requires set/array argument").as_str())),
};
Ok(json_remove_impl(&args[0], &filters))
}
fn filter(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
let name = "object.filter";
ensure_args_count(span, name, params, args, 2)?;