ephemeris_forge_github/api/model/rulesets/
head.rs1use facet::{Def, Facet, PrimitiveType, Shape, StructKind, Type, UserType};
2use facet_value::{VArray, Value, from_value, to_value};
3
4use crate::api::ForgeError;
5use crate::config::{MergeMethod, RuleDecoder};
6
7#[derive(Facet)]
8pub(super) struct RuleHead {
9 #[facet(rename = "type")]
10 pub(super) kind: String,
11 #[facet(default)]
12 parameters: Option<Value>,
13}
14
15impl RuleHead {
16 pub(super) const fn decoder<'a>(&'a self, ruleset: &'a str) -> HeadDecoder<'a> {
18 HeadDecoder {
19 head: self,
20 ruleset,
21 }
22 }
23
24 pub(super) fn decode(ruleset: &str, rule: &Value) -> Result<Self, ForgeError> {
25 from_value(rule.clone()).map_err(|source| ForgeError::RuleDecode {
26 ruleset: ruleset.to_owned(),
27 source: Box::new(source),
28 })
29 }
30
31 pub(super) fn has_entries(&self, key: &str) -> bool {
33 self.parameters
34 .as_ref()
35 .and_then(Value::as_object)
36 .and_then(|parameters| parameters.get(key))
37 .and_then(Value::as_array)
38 .is_some_and(|entries| !entries.is_empty())
39 }
40
41 pub(super) fn parameters<T: for<'a> Facet<'a>>(&self, ruleset: &str) -> Result<T, ForgeError> {
47 let mut parameters = self
48 .parameters
49 .clone()
50 .unwrap_or_else(|| Vec::<(String, Value)>::new().into_iter().collect());
51 prune(&mut parameters, T::SHAPE);
52 fill(&mut parameters, T::SHAPE);
53 from_value(parameters).map_err(|source| ForgeError::RuleParametersDecode {
54 ruleset: ruleset.to_owned(),
55 rule: self.kind.clone(),
56 source: Box::new(source),
57 })
58 }
59}
60
61pub(super) struct HeadDecoder<'a> {
62 head: &'a RuleHead,
63 ruleset: &'a str,
64}
65
66impl RuleDecoder for HeadDecoder<'_> {
67 type Error = ForgeError;
68
69 fn parameters<T: for<'a> Facet<'a>>(&self) -> Result<T, ForgeError> {
70 self.head.parameters(self.ruleset)
71 }
72}
73
74fn prune(value: &mut Value, shape: &'static Shape) {
75 let mut shape = shape;
76 while let Def::Option(option) = shape.def {
77 shape = option.t();
78 }
79 if let Def::List(list) = shape.def {
80 if let Some(entries) = value.as_array_mut() {
81 for entry in entries.iter_mut() {
82 prune(entry, list.t());
83 }
84 }
85 } else if let Type::User(UserType::Struct(declared)) = shape.ty
86 && declared.kind == StructKind::Struct
87 && let Some(object) = value.as_object_mut()
88 {
89 let keys: Vec<String> = object.keys().map(|key| key.as_str().to_owned()).collect();
90 for key in keys {
91 let field = declared
92 .fields
93 .iter()
94 .find(|field| field.effective_name() == key);
95 match (field, object.get_mut(&key)) {
96 (Some(field), Some(entry)) if !entry.is_null() => prune(entry, field.shape()),
97 _ => {
98 object.remove(&key);
99 }
100 }
101 }
102 }
103}
104
105fn fill(value: &mut Value, shape: &'static Shape) {
106 let mut shape = shape;
107 while let Def::Option(option) = shape.def {
108 shape = option.t();
109 }
110 if let Def::List(list) = shape.def {
111 if let Some(entries) = value.as_array_mut() {
112 for entry in entries.iter_mut() {
113 fill(entry, list.t());
114 }
115 }
116 } else if let Type::User(UserType::Struct(declared)) = shape.ty
117 && declared.kind == StructKind::Struct
118 && let Some(object) = value.as_object_mut()
119 {
120 for field in declared.fields {
121 let key = field.effective_name();
122 if let Some(entry) = object.get_mut(key) {
123 fill(entry, field.shape());
124 } else if let Some(default) = default_for(key, field.shape()) {
125 object.insert(key, default);
126 }
127 }
128 }
129}
130
131fn default_for(key: &str, shape: &'static Shape) -> Option<Value> {
132 if key == "allowed_merge_methods" {
134 return to_value(&MergeMethod::ALL).ok();
135 }
136 match (shape.def, shape.ty) {
137 (Def::Option(_), _) => Some(Value::NULL),
138 (Def::List(_), _) => Some(VArray::new().into()),
139 (_, Type::Primitive(PrimitiveType::Boolean)) => Some(Value::FALSE),
140 (_, Type::Primitive(PrimitiveType::Numeric(_))) => Some(Value::from(0u8)),
141 _ => None,
142 }
143}