refactor + size_cmp
This commit is contained in:
parent
a371b29b50
commit
a023352b0a
4 changed files with 129 additions and 8 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -19,7 +19,7 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
|||
|
||||
[[package]]
|
||||
name = "jsonfilter"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
dependencies = [
|
||||
"regex",
|
||||
"serde",
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
name = "jsonfilter"
|
||||
description = "Filter and compare JSON objects"
|
||||
authors = ["JMARyA <jmarya@hydrar.de>"]
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
edition = "2021"
|
||||
keywords = ["json", "filter"]
|
||||
license = "MIT"
|
||||
|
|
116
src/lib.rs
116
src/lib.rs
|
@ -68,7 +68,22 @@ pub fn order(a: &serde_json::Value, b: &serde_json::Value) -> std::cmp::Ordering
|
|||
}
|
||||
}
|
||||
|
||||
fn less_than_num(a: &serde_json::Value, b: &serde_json::Value) -> bool {
|
||||
/// Compares two `serde_json::Value` objects and determines if the first value is less than the second value.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `a` - A reference to the first `serde_json::Value` to be compared. This can be a floating-point number, integer, unsigned integer, or a string.
|
||||
/// * `b` - A reference to the second `serde_json::Value` to be compared. This should be of the same type as `a`.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `true` if `a` is less than `b`.
|
||||
/// * `false` otherwise.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will panic if `a` and `b` are not of the same type or if they are of an unsupported type.
|
||||
fn less_than_json(a: &serde_json::Value, b: &serde_json::Value) -> bool {
|
||||
if a.is_f64() {
|
||||
let a = a.as_f64().unwrap();
|
||||
let b = b.as_f64().unwrap();
|
||||
|
@ -90,7 +105,22 @@ fn less_than_num(a: &serde_json::Value, b: &serde_json::Value) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
fn greater_than_num(a: &serde_json::Value, b: &serde_json::Value) -> bool {
|
||||
/// Compares two `serde_json::Value` objects and determines if the first value is greater than the second value.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `a` - A reference to the first `serde_json::Value` to be compared. This can be a floating-point number, integer, unsigned integer, or a string.
|
||||
/// * `b` - A reference to the second `serde_json::Value` to be compared. This should be of the same type as `a`.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `true` if `a` is greater than `b`.
|
||||
/// * `false` otherwise.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will panic if `a` and `b` are not of the same type or if they are of an unsupported type.
|
||||
fn greater_than_json(a: &serde_json::Value, b: &serde_json::Value) -> bool {
|
||||
if a.is_f64() {
|
||||
let a = a.as_f64().unwrap();
|
||||
let b = b.as_f64().unwrap();
|
||||
|
@ -150,6 +180,18 @@ pub fn matches(filter: &serde_json::Value, obj: &serde_json::Value) -> bool {
|
|||
try_matches(filter, obj).unwrap()
|
||||
}
|
||||
|
||||
/// 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
|
||||
///
|
||||
/// * `Ok(true)` if the object matches the filter criteria.
|
||||
/// * `Ok(false)` if the object does not match the filter criteria.
|
||||
/// * `Err(FilterError)` if there is an error in the filtering process.
|
||||
pub fn try_matches(
|
||||
filter: &serde_json::Value,
|
||||
obj: &serde_json::Value,
|
||||
|
@ -157,6 +199,7 @@ pub fn try_matches(
|
|||
let filter = filter.as_object().unwrap();
|
||||
let obj_map = obj.as_object().unwrap();
|
||||
|
||||
// Handle the case where the filter has a single key, such as top level $and, $or, $not
|
||||
if filter.len() == 1 {
|
||||
let filter_keys: Vec<_> = filter.keys().collect();
|
||||
let op = filter_keys.first().unwrap();
|
||||
|
@ -209,6 +252,7 @@ pub fn try_matches(
|
|||
if val.is_object() {
|
||||
let val_keys: Vec<_> = val.as_object().unwrap().keys().collect();
|
||||
if val_keys.first().unwrap().starts_with('$') {
|
||||
// handle operators
|
||||
conditions.push(match_operator(val, obj, key.as_str()));
|
||||
} else {
|
||||
// nested
|
||||
|
@ -224,6 +268,7 @@ pub fn try_matches(
|
|||
continue;
|
||||
}
|
||||
|
||||
// Compare simple key-value pairs
|
||||
if let Some(valb) = obj_map.get(key) {
|
||||
if val != valb {
|
||||
conditions.push(Ok(false));
|
||||
|
@ -236,6 +281,21 @@ pub fn try_matches(
|
|||
check(&conditions)
|
||||
}
|
||||
|
||||
/// Checks if all conditions in the given list are met.
|
||||
///
|
||||
/// This function iterates through a list of `Result<bool, FilterError>` conditions, checking for errors first.
|
||||
/// If any condition is an error, it returns that error. Otherwise, it returns `Ok(true)` if all conditions are `true`
|
||||
/// and `Ok(false)` if any condition is `false`.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `conditions` - A slice of `Result<bool, FilterError>` representing the conditions to be checked.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(true)` if all conditions are true.
|
||||
/// * `Ok(false)` if any condition is false.
|
||||
/// * `Err(FilterError)` if any condition is an error.
|
||||
fn check(conditions: &[Result<bool, FilterError>]) -> Result<bool, FilterError> {
|
||||
conditions.iter().find(|x| x.is_err()).map_or_else(
|
||||
|| Ok(!conditions.iter().map(|x| x.unwrap()).any(|x| !x)),
|
||||
|
@ -243,6 +303,27 @@ fn check(conditions: &[Result<bool, FilterError>]) -> Result<bool, FilterError>
|
|||
)
|
||||
}
|
||||
|
||||
/// Matches a filter operator against a key-value pair in the object and determines if the condition is met.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `val` - The value associated with the operator in the filter. This should be a JSON object.
|
||||
/// * `raw_obj` - The object to be filtered. This should be a JSON object.
|
||||
/// * `key` - The key in the object that the filter operator applies to.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(true)` if the condition specified by the filter operator is met.
|
||||
/// * `Ok(false)` if the condition specified by the filter operator is not met.
|
||||
/// * `Err(FilterError)` if there is an error in the filtering process.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if:
|
||||
/// * The filter or object is not a valid JSON object.
|
||||
/// * An unknown operator is used in the filter.
|
||||
/// * A required key is not found in the object.
|
||||
/// * The filter format is invalid.
|
||||
fn match_operator(
|
||||
val: &serde_json::Value,
|
||||
raw_obj: &serde_json::Value,
|
||||
|
@ -250,6 +331,7 @@ fn match_operator(
|
|||
) -> Result<bool, FilterError> {
|
||||
let obj = raw_obj.as_object().unwrap();
|
||||
let val = val.as_object().unwrap();
|
||||
|
||||
if val.keys().len() == 1 {
|
||||
let keys: Vec<_> = val.keys().collect();
|
||||
let op = keys.first().unwrap().as_str();
|
||||
|
@ -283,25 +365,25 @@ fn match_operator(
|
|||
}
|
||||
"$lt" => {
|
||||
if let Some(a) = obj.get(key) {
|
||||
return Ok(less_than_num(a, op_arg));
|
||||
return Ok(less_than_json(a, op_arg));
|
||||
}
|
||||
return Err(FilterError::KeyNotFound);
|
||||
}
|
||||
"$lte" => {
|
||||
if let Some(a) = obj.get(key) {
|
||||
return Ok(less_than_num(a, op_arg) || a == op_arg);
|
||||
return Ok(less_than_json(a, op_arg) || a == op_arg);
|
||||
}
|
||||
return Err(FilterError::KeyNotFound);
|
||||
}
|
||||
"$gt" => {
|
||||
if let Some(valb) = obj.get(key) {
|
||||
return Ok(greater_than_num(valb, op_arg));
|
||||
return Ok(greater_than_json(valb, op_arg));
|
||||
}
|
||||
return Err(FilterError::KeyNotFound);
|
||||
}
|
||||
"$gte" => {
|
||||
if let Some(a) = obj.get(key) {
|
||||
return Ok(greater_than_num(a, op_arg) || a == op_arg);
|
||||
return Ok(greater_than_json(a, op_arg) || a == op_arg);
|
||||
}
|
||||
return Err(FilterError::KeyNotFound);
|
||||
}
|
||||
|
@ -352,6 +434,28 @@ fn match_operator(
|
|||
let pref_size = pref_size.as_u64().unwrap();
|
||||
return Ok(pref_size == val_size);
|
||||
}
|
||||
if let serde_json::Value::Object(s_op_obj) = op_arg {
|
||||
if s_op_obj.len() == 1 {
|
||||
let keys: Vec<_> = s_op_obj.keys().collect();
|
||||
let key = keys.first().unwrap();
|
||||
let val = s_op_obj.get(*key).unwrap().as_u64().unwrap();
|
||||
match key.as_str() {
|
||||
"$gt" => {
|
||||
return Ok(val_size > val);
|
||||
}
|
||||
"$gte" => {
|
||||
return Ok(val_size >= val);
|
||||
}
|
||||
"$lt" => {
|
||||
return Ok(val_size < val);
|
||||
}
|
||||
"$lte" => {
|
||||
return Ok(val_size <= val);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Err(FilterError::InvalidFilter);
|
||||
}
|
||||
return Err(FilterError::KeyNotFound);
|
||||
|
|
17
src/test.rs
17
src/test.rs
|
@ -359,6 +359,23 @@ mod tests {
|
|||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn array_size_cmp() {
|
||||
let empty: Vec<i32> = vec![];
|
||||
let one = vec![1];
|
||||
let two = vec![1, 2];
|
||||
let many = vec![1, 2, 3, 4, 5, 6];
|
||||
|
||||
let filter = json!({
|
||||
"list": {"$size": {"$gt": 0}}
|
||||
});
|
||||
|
||||
assert!(!matches(&filter, &json!({"list": empty})));
|
||||
assert!(matches(&filter, &json!({"list": one})));
|
||||
assert!(matches(&filter, &json!({"list": two})));
|
||||
assert!(matches(&filter, &json!({"list": many})));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested_modifier() {
|
||||
assert!(matches(
|
||||
|
|
Loading…
Add table
Reference in a new issue