use sha2::{Digest, Sha256};
use std::collections::BTreeMap;
pub(super) fn argument_filter_id(filter: &Option<BTreeMap<String, serde_json::Value>>) -> String {
match filter {
None => "static_no_args".to_string(),
Some(f) if f.is_empty() => "static_no_args".to_string(),
Some(f) => {
let json = python_compatible_json(f);
let hash = Sha256::digest(json.as_bytes());
format!("static_{:x}", hash)
}
}
}
pub(super) fn result_filter_id(filter: &Option<serde_json::Value>) -> String {
match filter {
None => "no_result_filter".to_string(),
Some(v) => {
let json = serde_json::to_string(v).unwrap_or_default();
let hash = Sha256::digest(json.as_bytes());
format!("{:x}", hash)
}
}
}
fn python_compatible_json(map: &BTreeMap<String, serde_json::Value>) -> String {
fn value_to_python_json(v: &serde_json::Value) -> String {
match v {
serde_json::Value::Null => "null".to_string(),
serde_json::Value::Bool(b) => {
if *b {
"true".to_string()
} else {
"false".to_string()
}
}
serde_json::Value::Number(n) => n.to_string(),
serde_json::Value::String(s) => {
format!("\"{}\"", s.replace('\\', "\\\\").replace('"', "\\\""))
}
serde_json::Value::Array(arr) => {
let items: Vec<String> = arr.iter().map(value_to_python_json).collect();
format!("[{}]", items.join(", "))
}
serde_json::Value::Object(obj) => {
let items: Vec<String> = obj
.iter()
.map(|(k, v)| {
format!(
"\"{}\": {}",
k.replace('\\', "\\\\").replace('"', "\\\""),
value_to_python_json(v)
)
})
.collect();
format!("{{{}}}", items.join(", "))
}
}
}
let items: Vec<String> = map
.iter()
.map(|(k, v)| {
format!(
"\"{}\": {}",
k.replace('\\', "\\\\").replace('"', "\\\""),
value_to_python_json(v)
)
})
.collect();
format!("{{{}}}", items.join(", "))
}
pub(super) fn check_argument_filter(
filter: &Option<BTreeMap<String, serde_json::Value>>,
args: &BTreeMap<String, String>,
) -> bool {
match filter {
None => true,
Some(expected) => expected.iter().all(|(k, v)| {
args.get(k)
.and_then(|s| serde_json::from_str::<serde_json::Value>(s).ok())
.is_some_and(|actual| &actual == v)
}),
}
}
pub(super) fn check_result_filter(
filter: &Option<serde_json::Value>,
result: &serde_json::Value,
) -> bool {
match filter {
None => true,
Some(expected) => expected == result,
}
}
pub(super) fn check_payload_filter(
filter: &Option<BTreeMap<String, serde_json::Value>>,
payload: &serde_json::Value,
) -> bool {
match filter {
None => true,
Some(expected) => {
let obj = match payload.as_object() {
Some(o) => o,
None => return expected.is_empty(),
};
expected.iter().all(|(k, v)| obj.get(k) == Some(v))
}
}
}