use serde_json::Value;
use super::framing::FramingOptions;
pub fn matches_frame(subject: &Value, frame: &Value, options: &FramingOptions) -> bool {
let frame_obj = match frame {
Value::Object(o) => o,
Value::Array(arr) => {
return arr.iter().any(|f| matches_frame(subject, f, options));
}
_ => return false,
};
if let Some(frame_types) = frame_obj.get("@type") {
if !matches_type(subject, frame_types) {
return false;
}
}
if let Some(frame_id) = frame_obj.get("@id") {
if !matches_id(subject, frame_id) {
return false;
}
}
let prop_entries: Vec<(&String, &Value)> = frame_obj
.iter()
.filter(|(k, _)| !k.starts_with('@'))
.collect();
if prop_entries.is_empty() {
return true; }
if options.require_all {
prop_entries
.iter()
.all(|(prop, fval)| matches_property(subject, prop, fval, options))
} else {
prop_entries
.iter()
.any(|(prop, fval)| matches_property(subject, prop, fval, options))
}
}
pub fn matches_type(subject: &Value, frame_types: &Value) -> bool {
let subj_types = collect_type_strings(subject);
let frame_type_set = collect_frame_type_strings(frame_types);
if frame_type_set.is_empty() {
return true; }
frame_type_set
.iter()
.any(|ft| subj_types.contains(&ft.as_str()))
}
pub fn matches_id(subject: &Value, frame_id: &Value) -> bool {
let subj_id = match subject.get("@id") {
Some(Value::String(s)) => s.as_str(),
_ => return false, };
match frame_id {
Value::String(fid) => subj_id == fid.as_str(),
Value::Array(ids) => ids
.iter()
.any(|v| matches!(v, Value::String(s) if s == subj_id)),
Value::Object(o) if o.is_empty() => true, _ => false,
}
}
pub fn matches_property(
subject: &Value,
prop: &str,
frame_value: &Value,
_options: &FramingOptions,
) -> bool {
let subj_values = match subject.get(prop) {
Some(Value::Array(arr)) => arr,
Some(single) => {
return matches_value_pattern(single, frame_value);
}
None => return false,
};
let frame_patterns: Vec<&Value> = match frame_value {
Value::Array(arr) => arr.iter().collect(),
other => vec![other],
};
subj_values.iter().any(|sv| {
frame_patterns
.iter()
.any(|fp| matches_value_pattern(sv, fp))
})
}
pub fn matches_value_pattern(value: &Value, pattern: &Value) -> bool {
match (value, pattern) {
(_, Value::Object(po)) if po.is_empty() => true,
(Value::Object(vo), Value::Object(po)) if po.contains_key("@value") => {
let pv = po.get("@value");
let vv = vo.get("@value");
if pv != vv {
return false;
}
if let Some(pt) = po.get("@type") {
if vo.get("@type") != Some(pt) {
return false;
}
}
if let Some(pl) = po.get("@language") {
if vo.get("@language") != Some(pl) {
return false;
}
}
true
}
(Value::Object(vo), Value::Object(po))
if po.contains_key("@id") && !po.contains_key("@value") =>
{
vo.get("@id") == po.get("@id")
}
(Value::Object(_), Value::Object(po)) if !po.contains_key("@id") => true,
(Value::String(vs), Value::String(ps)) => vs == ps,
(Value::Number(vn), Value::Number(pn)) => vn == pn,
(Value::Bool(vb), Value::Bool(pb)) => vb == pb,
(Value::Null, Value::Null) => true,
_ => false,
}
}
fn collect_type_strings(subject: &Value) -> Vec<&str> {
match subject.get("@type") {
Some(Value::Array(arr)) => arr.iter().filter_map(|v| v.as_str()).collect(),
Some(Value::String(s)) => vec![s.as_str()],
_ => vec![],
}
}
fn collect_frame_type_strings(frame_types: &Value) -> Vec<String> {
match frame_types {
Value::String(s) => vec![s.clone()],
Value::Array(arr) => arr
.iter()
.filter_map(|v| v.as_str().map(str::to_owned))
.collect(),
_ => vec![],
}
}