mod test;
use std::cmp::Ordering;
use serde_json::json;
#[must_use]
pub fn order(a: &serde_json::Value, b: &serde_json::Value) -> std::cmp::Ordering {
if matches(
&serde_json::json!({
"a": { "$gt": b}
}),
&serde_json::json!({
"a": a
}),
) {
Ordering::Greater
} else if matches(
&serde_json::json!({
"a": { "$lt": b}
}),
&serde_json::json!({
"a": a
}),
) {
Ordering::Less
} else if matches(
&serde_json::json!({
"a": b
}),
&serde_json::json!({
"a": a
}),
) {
Ordering::Equal
} else {
unreachable!()
}
}
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();
a < b
} else if a.is_i64() {
let a = a.as_i64().unwrap();
let b = b.as_i64().unwrap();
a < b
} else if a.is_u64() {
let a = a.as_u64().unwrap();
let b = b.as_u64().unwrap();
a < b
} else if a.is_string() {
let a = a.as_str().unwrap();
let b = b.as_str().unwrap();
a < b
} else {
unreachable!()
}
}
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();
a > b
} else if a.is_i64() {
let a = a.as_i64().unwrap();
let b = b.as_i64().unwrap();
a > b
} else if a.is_u64() {
let a = a.as_u64().unwrap();
let b = b.as_u64().unwrap();
a > b
} else if a.is_string() {
let a = a.as_str().unwrap();
let b = b.as_str().unwrap();
a > b
} else {
unreachable!()
}
}
#[derive(Debug, Clone, Copy)]
pub enum FilterError {
InvalidFilter,
UnknownOperator,
KeyNotFound,
}
#[must_use]
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_map = obj.as_object().unwrap();
if filter.len() == 1 {
let filter_keys: Vec<_> = filter.keys().collect();
let op = filter_keys.first().unwrap();
let op_arg = filter.get(op.as_str()).unwrap();
match op.as_str() {
"$and" => {
if let serde_json::Value::Array(and_list) = op_arg {
let and_list_bool: Vec<Result<bool, FilterError>> = and_list
.iter()
.map(|sub_filter| try_matches(sub_filter, obj))
.collect();
if let Some(err) = and_list_bool.iter().find(|x| x.is_err()) {
return *err;
}
return Ok(!and_list_bool.iter().map(|x| x.unwrap()).any(|x| !x));
}
return Err(FilterError::InvalidFilter);
}
"$or" => {
if let serde_json::Value::Array(or_list) = op_arg {
let or_list_bool: Vec<Result<bool, FilterError>> = or_list
.iter()
.map(|sub_filter| try_matches(sub_filter, obj))
.collect();
if let Some(err) = or_list_bool.iter().find(|x| x.is_err()) {
return *err;
}
return Ok(or_list_bool.iter().map(|x| x.unwrap()).any(|x| x));
}
return Err(FilterError::InvalidFilter);
}
"$not" => {
if let Some(inner) = filter.get("$not") {
let new_filter = inner;
return Ok(!try_matches(new_filter, obj)?);
}
return Err(FilterError::InvalidFilter);
}
_ => {
if op.starts_with('$') {
return Err(FilterError::UnknownOperator);
}
}
}
}
let mut conditions = vec![];
for (key, val) in filter {
if val.is_object() {
let val_keys: Vec<_> = val.as_object().unwrap().keys().collect();
if val_keys.first().unwrap().starts_with('$') {
conditions.push(match_operator(val, obj, key.as_str()));
} else {
for (_, _) in val.as_object().unwrap() {
let new_filter = filter.get(key).unwrap();
if let Some(val) = obj_map.get(key) {
conditions.push(try_matches(new_filter, val));
} else {
return Err(FilterError::KeyNotFound);
}
}
}
continue;
}
if let Some(valb) = obj_map.get(key) {
if val != valb {
conditions.push(Ok(false));
}
} else {
return Err(FilterError::KeyNotFound);
}
}
check(&conditions)
}
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)),
|possible_error| *possible_error,
)
}
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 {
let keys: Vec<_> = val.keys().collect();
let op = keys.first().unwrap().as_str();
let op_arg = val.get(op).unwrap();
match op {
"$and" => {
if let serde_json::Value::Array(and_list) = op_arg {
let and_list_bool: Vec<Result<bool, FilterError>> = and_list
.iter()
.map(|sub_filter| try_matches(sub_filter, raw_obj))
.collect();
if let Some(err) = and_list_bool.iter().find(|x| x.is_err()) {
return *err;
}
return Ok(!and_list_bool.iter().map(|x| x.unwrap()).any(|x| !x));
}
return Err(FilterError::InvalidFilter);
}
"$or" => {
if let serde_json::Value::Array(or_list) = op_arg {
let or_list_bool: Vec<Result<bool, FilterError>> = or_list
.iter()
.map(|sub_filter| try_matches(sub_filter, raw_obj))
.collect();
if let Some(err) = or_list_bool.iter().find(|x| x.is_err()) {
return *err;
}
return Ok(or_list_bool.iter().map(|x| x.unwrap()).any(|x| x));
}
return Err(FilterError::InvalidFilter);
}
"$lt" => {
if let Some(a) = obj.get(key) {
return Ok(less_than_json(a, op_arg));
}
return Err(FilterError::KeyNotFound);
}
"$lte" => {
if let Some(a) = obj.get(key) {
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_json(valb, op_arg));
}
return Err(FilterError::KeyNotFound);
}
"$gte" => {
if let Some(a) = obj.get(key) {
return Ok(greater_than_json(a, op_arg) || a == op_arg);
}
return Err(FilterError::KeyNotFound);
}
"$not" => {
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)?);
}
return Err(FilterError::InvalidFilter);
}
"$ne" => {
if let Some(valb) = obj.get(key) {
return Ok(valb != op_arg);
}
return Err(FilterError::KeyNotFound);
}
"$in" => {
if let Some(valb) = obj.get(key) {
if let serde_json::Value::Array(list) = valb {
return Ok(list.iter().any(|x| x == op_arg));
}
return Err(FilterError::InvalidFilter);
}
return Err(FilterError::KeyNotFound);
}
"$nin" => {
if let Some(valb) = obj.get(key) {
if let serde_json::Value::Array(list) = valb {
return Ok(!list.iter().any(|x| x == op_arg));
}
return Err(FilterError::InvalidFilter);
}
return Err(FilterError::KeyNotFound);
}
"$exists" => {
if let serde_json::Value::Bool(exists) = op_arg {
let valb = obj.get(key).is_some();
return Ok(*exists == valb);
}
return Err(FilterError::InvalidFilter);
}
"$size" => {
if let Some(serde_json::Value::Array(list)) = obj.get(key) {
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 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);
}
"$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 Ok(pattern.is_match(valb));
}
return Err(FilterError::KeyNotFound);
}
return Err(FilterError::InvalidFilter);
}
"$type" => {
if let Some(valb) = obj.get(key) {
if let serde_json::Value::String(type_str) = op_arg {
return Ok(match type_str.to_lowercase().as_str() {
"null" => valb.is_null(),
"string" => valb.is_string(),
"number" => valb.is_number(),
"object" => valb.is_object(),
"array" => valb.is_array(),
"boolean" => valb.is_boolean(),
_ => false,
});
}
return Err(FilterError::InvalidFilter);
}
return Err(FilterError::KeyNotFound);
}
_ => {
if op.starts_with('$') {
return Err(FilterError::UnknownOperator);
}
}
}
}
Err(FilterError::InvalidFilter)
}