use serde_json::{Value as JsonValue, json};
use starlark::values::list::ListRef;
use starlark::values::{Heap, Value};
#[derive(Debug, Clone)]
pub struct MatchTreeNode {
pub json: JsonValue,
}
impl serde::Serialize for MatchTreeNode {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.json.serialize(serializer)
}
}
fn set_children_on_deepest_leaf(json: &mut JsonValue, children: Vec<JsonValue>) {
if let Some(obj) = json.as_object_mut()
&& let Some(cond) = obj.get_mut("condition").and_then(|c| c.as_object_mut())
{
if let Some(existing) = cond.get_mut("children").and_then(|c| c.as_array_mut()) {
if existing.is_empty() {
*existing = children;
return;
}
if existing.len() == 1 && existing[0].get("condition").is_some() {
set_children_on_deepest_leaf(&mut existing[0], children);
return;
}
}
cond.insert("children".into(), serde_json::json!(children));
}
}
pub fn set_children_on_deepest_leaf_pub(json: &mut JsonValue, children: Vec<JsonValue>) {
set_children_on_deepest_leaf(json, children);
}
pub fn mt_condition_with_doc(
observe: JsonValue,
pattern: JsonValue,
doc: Option<String>,
source: Option<String>,
) -> MatchTreeNode {
let mut condition = json!({
"observe": observe,
"pattern": pattern,
"children": []
});
if let Some(d) = doc {
condition
.as_object_mut()
.unwrap()
.insert("doc".to_string(), json!(d));
}
if let Some(s) = source {
condition
.as_object_mut()
.unwrap()
.insert("source".to_string(), json!(s));
}
MatchTreeNode {
json: json!({"condition": condition}),
}
}
pub fn pattern_to_json<'v>(value: Value<'v>, heap: &'v Heap) -> anyhow::Result<JsonValue> {
if value.is_none() {
return Ok(json!("wildcard"));
}
if let Some(s) = value.unpack_str() {
return Ok(json!({"literal": {"literal": s}}));
}
if let Some(list) = ListRef::from_value(value) {
let items: Result<Vec<_>, _> = list.iter().map(|v| pattern_to_json(v, heap)).collect();
return Ok(json!({"any_of": items?}));
}
if let Some(tuple) = starlark::values::tuple::TupleRef::from_value(value) {
let items: Result<Vec<_>, _> = tuple.iter().map(|v| pattern_to_json(v, heap)).collect();
return Ok(json!({"any_of": items?}));
}
if value.get_type() == "struct"
&& let Ok(Some(regex_val)) = value.get_attr("_regex", heap)
&& let Some(s) = regex_val.unpack_str()
{
return Ok(json!({"regex": s}));
}
if value.get_type() == "struct"
&& let Ok(Some(mk_val)) = value.get_attr("_match_key", heap)
&& mk_val.unpack_str() == Some("glob")
&& let Ok(Some(mv_val)) = value.get_attr("_match_value", heap)
&& let Some(pat) = mv_val.unpack_str()
{
if pat == "*" || pat == "**" {
return Ok(json!("wildcard"));
}
if let Some(stripped) = pat.strip_suffix("/**/*") {
return Ok(json!({"prefix": {"literal": stripped}}));
}
if let Some(stripped) = pat.strip_suffix("/**") {
return Ok(json!({"prefix": {"literal": stripped}}));
}
if let Some(stripped) = pat.strip_suffix("/*") {
return Ok(json!({"child_of": {"literal": stripped}}));
}
return Ok(json!({"prefix": {"literal": pat}}));
}
if value.get_type() == "struct"
&& let Ok(Some(glob_val)) = value.get_attr("_glob", heap)
&& let Some(s) = glob_val.unpack_str()
{
let glob_type = value
.get_attr("_glob_type", heap)
.ok()
.flatten()
.and_then(|v| v.unpack_str())
.unwrap_or("recursive");
return match glob_type {
"wildcard" => Ok(json!("wildcard")),
"children" => Ok(json!({"child_of": {"literal": s}})),
_ => Ok(json!({"prefix": {"literal": s}})),
};
}
anyhow::bail!(
"cannot convert {} to a match tree pattern: {}",
value.get_type(),
value.to_repr()
)
}