diff --git a/src/lib.rs b/src/lib.rs index 8fa8e9a..7a02330 100644 --- a/src/lib.rs +++ b/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 { 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 = 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 = 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 { 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); } diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index 06e3aae..0000000 --- a/src/main.rs +++ /dev/null @@ -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 - }) - )); -}