error handling
This commit is contained in:
parent
c9d1c372c9
commit
efed4c0bea
2 changed files with 99 additions and 90 deletions
159
src/lib.rs
159
src/lib.rs
|
@ -1,6 +1,5 @@
|
|||
use serde_json::json;
|
||||
|
||||
mod test;
|
||||
use serde_json::json;
|
||||
|
||||
fn less_than_num(a: &serde_json::Value, b: &serde_json::Value) -> bool {
|
||||
if a.is_f64() {
|
||||
|
@ -38,9 +37,49 @@ fn greater_than_num(a: &serde_json::Value, b: &serde_json::Value) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn matches(filter: &serde_json::Value, raw_obj: &serde_json::Value) -> bool {
|
||||
#[derive(Debug)]
|
||||
/// Represents errors that can occur while processing filters.
|
||||
pub enum FilterError {
|
||||
/// Indicates that the schema of the filter is invalid.
|
||||
InvalidFilter,
|
||||
/// Indicates that an unknown operator was encountered in the filter.
|
||||
UnknownOperator,
|
||||
/// Indicates that a key was not found in the object being filtered.
|
||||
KeyNotFound,
|
||||
}
|
||||
|
||||
/// Matches a filter against a raw object and returns a boolean indicating if the filter matches the object.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `filter` - A reference to a `serde_json::Value` representing the filter to apply. Must be an object.
|
||||
/// * `obj` - A reference to a `serde_json::Value` representing the object to match against. Must be an object.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns `true` if the filter matches the object, otherwise `false`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use serde_json::json;
|
||||
/// use jsonfilter::matches;
|
||||
///
|
||||
/// let filter = json!({"name": "John", "age": 30});
|
||||
/// let obj = json!({"name": "John", "age": 30, "city": "New York"});
|
||||
///
|
||||
/// assert!(matches(&filter, &obj));
|
||||
/// ```
|
||||
pub fn matches(filter: &serde_json::Value, obj: &serde_json::Value) -> bool {
|
||||
try_matches(filter, obj).unwrap()
|
||||
}
|
||||
|
||||
pub fn try_matches(
|
||||
filter: &serde_json::Value,
|
||||
obj: &serde_json::Value,
|
||||
) -> Result<bool, FilterError> {
|
||||
let filter = filter.as_object().unwrap();
|
||||
let obj = raw_obj.as_object().unwrap();
|
||||
let obj_map = obj.as_object().unwrap();
|
||||
|
||||
if filter.len() == 1 {
|
||||
let filter_keys: Vec<_> = filter.keys().collect();
|
||||
|
@ -51,27 +90,27 @@ pub fn matches(filter: &serde_json::Value, raw_obj: &serde_json::Value) -> bool
|
|||
if let serde_json::Value::Array(and_list) = op_arg {
|
||||
let and_list_bool: Vec<bool> = and_list
|
||||
.iter()
|
||||
.map(|sub_filter| matches(sub_filter, raw_obj))
|
||||
.map(|sub_filter| matches(sub_filter, obj))
|
||||
.collect();
|
||||
return !and_list_bool.iter().any(|x| !x);
|
||||
return Ok(!and_list_bool.iter().any(|x| !x));
|
||||
} else {
|
||||
return false;
|
||||
return Err(FilterError::InvalidFilter);
|
||||
}
|
||||
}
|
||||
"$or" => {
|
||||
if let serde_json::Value::Array(or_list) = op_arg {
|
||||
let or_list_bool: Vec<bool> = or_list
|
||||
.iter()
|
||||
.map(|sub_filter| matches(sub_filter, raw_obj))
|
||||
.map(|sub_filter| matches(sub_filter, obj))
|
||||
.collect();
|
||||
return or_list_bool.iter().any(|x| *x);
|
||||
return Ok(or_list_bool.iter().any(|x| *x));
|
||||
} else {
|
||||
return false;
|
||||
return Err(FilterError::InvalidFilter);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if op.starts_with("$") {
|
||||
unimplemented!()
|
||||
return Err(FilterError::UnknownOperator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -81,33 +120,37 @@ pub fn matches(filter: &serde_json::Value, raw_obj: &serde_json::Value) -> bool
|
|||
if val.is_object() {
|
||||
let val_keys: Vec<_> = val.as_object().unwrap().keys().collect();
|
||||
if val_keys.first().unwrap().starts_with("$") {
|
||||
return match_operator(val, raw_obj, key.as_str());
|
||||
return match_operator(val, obj, key.as_str());
|
||||
} else {
|
||||
// nested
|
||||
for (_, _) in val.as_object().unwrap() {
|
||||
let new_filter = filter.get(key).unwrap();
|
||||
if let Some(val) = obj.get(key) {
|
||||
return matches(new_filter, val);
|
||||
if let Some(val) = obj_map.get(key) {
|
||||
return try_matches(new_filter, val);
|
||||
} else {
|
||||
return false;
|
||||
return Err(FilterError::KeyNotFound);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(valb) = obj.get(key) {
|
||||
if let Some(valb) = obj_map.get(key) {
|
||||
if val != valb {
|
||||
return false;
|
||||
return Ok(false);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
return Err(FilterError::KeyNotFound);
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn match_operator(val: &serde_json::Value, raw_obj: &serde_json::Value, key: &str) -> bool {
|
||||
fn match_operator(
|
||||
val: &serde_json::Value,
|
||||
raw_obj: &serde_json::Value,
|
||||
key: &str,
|
||||
) -> Result<bool, FilterError> {
|
||||
let obj = raw_obj.as_object().unwrap();
|
||||
let val = val.as_object().unwrap();
|
||||
if val.keys().len() == 1 {
|
||||
|
@ -117,81 +160,77 @@ fn match_operator(val: &serde_json::Value, raw_obj: &serde_json::Value, key: &st
|
|||
match op {
|
||||
"$lt" => {
|
||||
if let Some(a) = obj.get(key) {
|
||||
return less_than_num(a, op_arg);
|
||||
return Ok(less_than_num(a, op_arg));
|
||||
} else {
|
||||
return false;
|
||||
return Err(FilterError::KeyNotFound);
|
||||
}
|
||||
}
|
||||
"$lte" => {
|
||||
if let Some(a) = obj.get(key) {
|
||||
return less_than_num(a, op_arg) || a == op_arg;
|
||||
return Ok(less_than_num(a, op_arg) || a == op_arg);
|
||||
} else {
|
||||
return false;
|
||||
return Err(FilterError::KeyNotFound);
|
||||
}
|
||||
}
|
||||
"$gt" => {
|
||||
if let Some(valb) = obj.get(key) {
|
||||
return greater_than_num(valb, op_arg);
|
||||
return Ok(greater_than_num(valb, op_arg));
|
||||
} else {
|
||||
return false;
|
||||
return Err(FilterError::KeyNotFound);
|
||||
}
|
||||
}
|
||||
"$gte" => {
|
||||
if let Some(a) = obj.get(key) {
|
||||
return greater_than_num(a, op_arg) || a == op_arg;
|
||||
return Ok(greater_than_num(a, op_arg) || a == op_arg);
|
||||
} else {
|
||||
return false;
|
||||
return Err(FilterError::KeyNotFound);
|
||||
}
|
||||
}
|
||||
"$not" => {
|
||||
if let Some(inner) = val.get("$not") {
|
||||
if let serde_json::Value::Object(inner) = inner {
|
||||
let new_filter = json!({
|
||||
key: inner
|
||||
});
|
||||
return !matches(&new_filter, raw_obj);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
if let Some(serde_json::Value::Object(inner)) = val.get("$not") {
|
||||
let new_filter = json!({
|
||||
key: inner
|
||||
});
|
||||
return Ok(!try_matches(&new_filter, raw_obj)?);
|
||||
} else {
|
||||
return false;
|
||||
return Err(FilterError::InvalidFilter);
|
||||
}
|
||||
}
|
||||
"$ne" => {
|
||||
if let Some(valb) = obj.get(key) {
|
||||
return valb != op_arg;
|
||||
return Ok(valb != op_arg);
|
||||
} else {
|
||||
return false;
|
||||
return Err(FilterError::KeyNotFound);
|
||||
}
|
||||
}
|
||||
"$in" => {
|
||||
if let Some(valb) = obj.get(key) {
|
||||
if let serde_json::Value::Array(list) = valb {
|
||||
return list.iter().any(|x| x == op_arg);
|
||||
return Ok(list.iter().any(|x| x == op_arg));
|
||||
} else {
|
||||
return false;
|
||||
return Err(FilterError::InvalidFilter);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
return Err(FilterError::KeyNotFound);
|
||||
}
|
||||
}
|
||||
"$nin" => {
|
||||
if let Some(valb) = obj.get(key) {
|
||||
if let serde_json::Value::Array(list) = valb {
|
||||
return !list.iter().any(|x| x == op_arg);
|
||||
return Ok(!list.iter().any(|x| x == op_arg));
|
||||
} else {
|
||||
return false;
|
||||
return Err(FilterError::InvalidFilter);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
return Err(FilterError::KeyNotFound);
|
||||
}
|
||||
}
|
||||
"$exists" => {
|
||||
if let serde_json::Value::Bool(exists) = op_arg {
|
||||
let valb = obj.get(key).is_some();
|
||||
return *exists == valb;
|
||||
return Ok(*exists == valb);
|
||||
} else {
|
||||
return false;
|
||||
return Err(FilterError::InvalidFilter);
|
||||
}
|
||||
}
|
||||
"$size" => {
|
||||
|
@ -199,30 +238,30 @@ fn match_operator(val: &serde_json::Value, raw_obj: &serde_json::Value, key: &st
|
|||
let val_size = list.len() as u64;
|
||||
if let serde_json::Value::Number(pref_size) = op_arg {
|
||||
let pref_size = pref_size.as_u64().unwrap();
|
||||
return pref_size == val_size;
|
||||
return Ok(pref_size == val_size);
|
||||
} else {
|
||||
return false;
|
||||
return Err(FilterError::InvalidFilter);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
return Err(FilterError::KeyNotFound);
|
||||
}
|
||||
}
|
||||
"$regex" => {
|
||||
if let serde_json::Value::String(regex_pattern) = op_arg {
|
||||
if let Some(serde_json::Value::String(valb)) = obj.get(key) {
|
||||
let pattern = regex::Regex::new(regex_pattern).unwrap();
|
||||
return pattern.is_match(valb);
|
||||
return Ok(pattern.is_match(valb));
|
||||
} else {
|
||||
return false;
|
||||
return Err(FilterError::KeyNotFound);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
return Err(FilterError::InvalidFilter);
|
||||
}
|
||||
}
|
||||
"$type" => {
|
||||
if let Some(valb) = obj.get(key) {
|
||||
if let serde_json::Value::String(type_str) = op_arg {
|
||||
return match type_str.to_lowercase().as_str() {
|
||||
return Ok(match type_str.to_lowercase().as_str() {
|
||||
"null" => valb.is_null(),
|
||||
"string" => valb.is_string(),
|
||||
"number" => valb.is_number(),
|
||||
|
@ -230,21 +269,21 @@ fn match_operator(val: &serde_json::Value, raw_obj: &serde_json::Value, key: &st
|
|||
"array" => valb.is_array(),
|
||||
"boolean" => valb.is_boolean(),
|
||||
_ => false,
|
||||
};
|
||||
});
|
||||
} else {
|
||||
return false;
|
||||
return Err(FilterError::InvalidFilter);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
return Err(FilterError::KeyNotFound);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if op.starts_with("$") {
|
||||
unimplemented!()
|
||||
return Err(FilterError::UnknownOperator);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return Ok(false);
|
||||
}
|
||||
|
|
30
src/main.rs
30
src/main.rs
|
@ -1,30 +0,0 @@
|
|||
use jsonfilter::matches;
|
||||
use serde_json::json;
|
||||
|
||||
fn main() {
|
||||
assert!(matches(
|
||||
&json!({
|
||||
"$and": [
|
||||
{ "key": "value" },
|
||||
{ "num": 5 }
|
||||
]
|
||||
}),
|
||||
&json!({
|
||||
"key": "value",
|
||||
"num": 5
|
||||
})
|
||||
));
|
||||
|
||||
assert!(!matches(
|
||||
&json!({
|
||||
"$and": [
|
||||
{ "key": "value" },
|
||||
{ "num": 5 }
|
||||
]
|
||||
}),
|
||||
&json!({
|
||||
"key": "value",
|
||||
"num": 3
|
||||
})
|
||||
));
|
||||
}
|
Loading…
Add table
Reference in a new issue