Skip to content

Commit

Permalink
feat: support optional json path as first argument on RadonOpCodes::S…
Browse files Browse the repository at this point in the history
…tringParseJSONArray
  • Loading branch information
guidiaz committed Jan 10, 2024
1 parent f222ee5 commit 1afc7c1
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 10 deletions.
98 changes: 89 additions & 9 deletions rad/src/operators/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,87 @@ pub fn parse_json_map(input: &RadonString, args: &Option<Vec<Value>>) -> Result<
},
_ => Err(wrong_args())
}
}

pub fn parse_json_array(input: &RadonString, args: &Option<Vec<Value>>) -> Result<RadonArray, RadError> {
let wrong_args = || RadError::WrongArguments {
input_type: RadonString::radon_type_name(),
operator: "ParseJsonArray".to_string(),
args: args.to_owned().unwrap_or_default(),
};

let json_input: JsonValue = serde_json::from_str(&input.value())
.map_err(|err| RadError::JsonParse {
description: err.to_string(),
})?;

match args.to_owned().unwrap_or_default().get(0) {
Some(Value::Array(values)) => {
let mut items: Vec<RadonTypes> = vec![];
for path in values {
if let Value::Text(json_path) = path {
let selector = Selector::new(json_path.as_str())
.map_err(|err| RadError::JsonPathParse {
description: err.to_string(),
})?;
let mut subitems: Vec<RadonTypes> = selector.find(&json_input)
.map(|item| into_radon_types(item))
.collect();
if subitems.len() > 1 {
items.insert(items.len(), RadonArray::from(subitems).into());
} else {
items.append(subitems.as_mut());
}
} else {
return Err(wrong_args());
}
}
Ok(RadonArray::from(items))
}
Some(Value::Text(json_path)) => {
let selector = Selector::new(json_path.as_str())
.map_err(|err| RadError::JsonPathParse {
description: err.to_string(),
})?;
let items: Vec<RadonTypes> = selector.find(&json_input)
.map(|item| into_radon_types(item))
.collect();
Ok(RadonArray::from(items))
}
None => {
RadonTypes::try_from(json_input)?.try_into()
}
_ => Err(wrong_args())
}
}

fn into_radon_types(value: &serde_json::Value) -> RadonTypes {
match value {
serde_json::Value::Number(value) => {
if value.is_f64() {
RadonTypes::from(RadonFloat::from(value.as_f64().unwrap_or_default()))
} else {
RadonTypes::from(RadonInteger::from(value.as_i64().unwrap_or_default() as i128))
}
},
serde_json::Value::Bool(value) => RadonTypes::from(RadonBoolean::from(*value)),
serde_json::Value::String(value) => RadonTypes::from(RadonString::from(value.clone())),
serde_json::Value::Object(entries) => {
let mut object: BTreeMap<String, RadonTypes> = BTreeMap::new();
for (key, value) in entries {
object.insert(key.clone(), into_radon_types(value));
}
RadonTypes::from(RadonMap::from(object))
}
serde_json::Value::Array(values) => {
let items: Vec<RadonTypes> = values
.iter()
.map(|item| into_radon_types(item))
.collect();
RadonTypes::from(RadonArray::from(items))
}
_ => RadonTypes::from(RadonError::new(RadError::JsonParse { description: value.to_string() }))
}
}

fn add_children(
Expand Down Expand Up @@ -477,7 +557,7 @@ mod tests {
#[test]
fn test_parse_json_map() {
let json_map = RadonString::from(r#"{ "Hello": "world" }"#);
let output = parse_json_map(&json_map).unwrap();
let output = parse_json_map(&json_map, &None).unwrap();

let key = "Hello";
let value = RadonTypes::String(RadonString::from("world"));
Expand Down Expand Up @@ -637,7 +717,7 @@ mod tests {
fn test_parse_json_map_with_null_entries() {
// When parsing a JSON map, any keys with value `null` are ignored
let json_map = RadonString::from(r#"{ "Hello": "world", "Bye": null }"#);
let output = parse_json_map(&json_map).unwrap();
let output = parse_json_map(&json_map, &None).unwrap();

let key = "Hello";
let value = RadonTypes::String(RadonString::from("world"));
Expand All @@ -651,15 +731,15 @@ mod tests {
#[test]
fn test_parse_json_map_fail() {
let invalid_json = RadonString::from(r#"{ "Hello": }"#);
let output = parse_json_map(&invalid_json).unwrap_err();
let output = parse_json_map(&invalid_json, &None).unwrap_err();

let expected_err = RadError::JsonParse {
description: "expected value at line 1 column 13".to_string(),
};
assert_eq!(output, expected_err);

let json_array = RadonString::from(r#"[1,2,3]"#);
let output = parse_json_map(&json_array).unwrap_err();
let output = parse_json_map(&json_array, &None).unwrap_err();
let expected_err = RadError::Decode {
from: "cbor::value::Value",
to: RadonMap::radon_type_name(),
Expand All @@ -670,7 +750,7 @@ mod tests {
#[test]
fn test_parse_json_array() {
let json_array = RadonString::from(r#"[1,2,3]"#);
let output = parse_json_array(&json_array).unwrap();
let output = parse_json_array(&json_array, &None).unwrap();

let expected_output = RadonArray::from(vec![
RadonTypes::Integer(RadonInteger::from(1)),
Expand All @@ -685,7 +765,7 @@ mod tests {
fn test_parse_json_array_with_null_entries() {
// When parsing a JSON array, any elements with value `null` are ignored
let json_array = RadonString::from(r#"[null, 1, null, null, 2, 3, null]"#);
let output = parse_json_array(&json_array).unwrap();
let output = parse_json_array(&json_array, &None).unwrap();

let expected_output = RadonArray::from(vec![
RadonTypes::Integer(RadonInteger::from(1)),
Expand All @@ -699,15 +779,15 @@ mod tests {
#[test]
fn test_parse_json_array_fail() {
let invalid_json = RadonString::from(r#"{ "Hello": }"#);
let output = parse_json_array(&invalid_json).unwrap_err();
let output = parse_json_array(&invalid_json, &None).unwrap_err();

let expected_err = RadError::JsonParse {
description: "expected value at line 1 column 13".to_string(),
};
assert_eq!(output, expected_err);

let json_map = RadonString::from(r#"{ "Hello": "world" }"#);
let output = parse_json_array(&json_map).unwrap_err();
let output = parse_json_array(&json_map, &None).unwrap_err();
let expected_err = RadError::Decode {
from: "cbor::value::Value",
to: RadonArray::radon_type_name(),
Expand Down Expand Up @@ -1235,7 +1315,7 @@ mod tests {
let args = vec![Value::Map(map)];

let result = string_match(&input_key, &args);
assert_eq!(result.unwrap_err().to_string(), "Wrong `RadonString::String match()` arguments: `[Map({Text(\"key1\"): Float(1.0), Text(\"key2\"): Float(2.0)})]`");
assert_eq!(result.unwrap_err().to_string(), "Wrong `RadonString::StringMatch()` arguments: `[Map({Text(\"key1\"): Float(1.0), Text(\"key2\"): Float(2.0)})]`");
}

#[test]
Expand Down
2 changes: 1 addition & 1 deletion rad/src/types/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ impl Operable for RadonString {
(RadonOpCodes::StringMatch, Some(args)) => {
string_operators::string_match(self, args.as_slice()).map(RadonTypes::from)
}
(RadonOpCodes::StringParseJSONArray, None) => string_operators::parse_json_array(self)
(RadonOpCodes::StringParseJSONArray, args) => string_operators::parse_json_array(self, &args)
.map(RadonTypes::from)
.map_err(Into::into),
(RadonOpCodes::StringParseJSONMap, args) => string_operators::parse_json_map(self, &args)
Expand Down

0 comments on commit 1afc7c1

Please sign in to comment.