1use ahash::HashMap;
2use serde::{Deserialize, Deserializer, Serialize};
3use serde_json::Value;
4use std::sync::Arc;
5
6#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
8#[serde(rename_all = "camelCase")]
9pub struct BaseDecisionContent {
10 pub nodes: Vec<Arc<DecisionNode>>,
11 pub edges: Vec<Arc<DecisionEdge>>,
12}
13
14#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
15#[serde(rename_all = "camelCase")]
16pub struct DecisionEdge {
17 pub id: Arc<str>,
18 pub source_id: Arc<str>,
19 pub target_id: Arc<str>,
20 pub source_handle: Option<Arc<str>>,
21}
22
23#[derive(Clone, Debug, Deserialize, Serialize)]
24#[serde(rename_all = "camelCase")]
25pub struct DecisionNode {
26 pub id: Arc<str>,
27 pub name: Arc<str>,
28 #[serde(rename = "type")]
29 #[serde(flatten)]
30 pub kind: DecisionNodeKind,
31}
32
33impl PartialEq for DecisionNode {
34 fn eq(&self, other: &Self) -> bool {
35 self.id == other.id
36 }
37}
38
39#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
40#[serde(tag = "type")]
41#[serde(rename_all = "camelCase")]
42pub enum DecisionNodeKind {
43 InputNode {
44 #[serde(default)]
45 content: InputNodeContent,
46 },
47 OutputNode {
48 #[serde(default)]
49 content: OutputNodeContent,
50 },
51 FunctionNode {
52 content: FunctionNodeContent,
53 },
54 DecisionNode {
55 content: DecisionNodeContent,
56 },
57 DecisionTableNode {
58 content: DecisionTableContent,
59 },
60 ExpressionNode {
61 content: ExpressionNodeContent,
62 },
63 SwitchNode {
64 content: SwitchNodeContent,
65 },
66 CustomNode {
67 content: CustomNodeContent,
68 },
69}
70
71#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Default)]
72#[serde(rename_all = "camelCase")]
73pub struct InputNodeContent {
74 #[serde(default, deserialize_with = "empty_value_string_is_none_safe")]
75 pub schema: Option<Arc<Value>>,
76}
77
78#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Default)]
79#[serde(rename_all = "camelCase")]
80pub struct OutputNodeContent {
81 #[serde(default, deserialize_with = "empty_value_string_is_none_safe")]
82 pub schema: Option<Arc<Value>>,
83}
84
85#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
86#[serde(rename_all = "camelCase")]
87#[serde(untagged)]
88pub enum FunctionNodeContent {
89 Version2(FunctionContent),
90 Version1(Arc<str>),
91}
92
93#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Default)]
94#[serde(rename_all = "camelCase")]
95pub struct FunctionContent {
96 pub source: Arc<str>,
97}
98
99#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
100#[serde(rename_all = "camelCase")]
101pub struct DecisionNodeContent {
102 pub key: Arc<str>,
103 #[serde(flatten)]
104 pub transform_attributes: TransformAttributes,
105}
106
107#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
108#[serde(rename_all = "camelCase")]
109pub struct DecisionTableContent {
110 #[serde(deserialize_with = "deserialize_trim_rules")]
111 pub rules: Arc<Vec<HashMap<Arc<str>, Arc<str>>>>,
112 pub inputs: Arc<Vec<DecisionTableInputField>>,
113 pub outputs: Arc<Vec<DecisionTableOutputField>>,
114 pub hit_policy: DecisionTableHitPolicy,
115 #[serde(flatten)]
116 pub transform_attributes: TransformAttributes,
117}
118
119#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)]
120#[serde(rename_all = "camelCase")]
121pub enum DecisionTableHitPolicy {
122 #[default]
123 First,
124 Collect,
125}
126
127#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
128#[serde(rename_all = "camelCase")]
129pub struct DecisionTableInputField {
130 pub id: Arc<str>,
131 #[serde(default = "empty_arc_str")]
132 pub name: Arc<str>,
133 #[serde(default, deserialize_with = "empty_string_is_none")]
134 pub field: Option<Arc<str>>,
135}
136
137#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
138#[serde(rename_all = "camelCase")]
139pub struct DecisionTableOutputField {
140 pub id: Arc<str>,
141 #[serde(default = "empty_arc_str")]
142 pub name: Arc<str>,
143 pub field: Arc<str>,
144 #[serde(
145 rename = "type",
146 default,
147 skip_serializing_if = "Option::is_none",
148 deserialize_with = "empty_string_is_none"
149 )]
150 pub column_type: Option<Arc<str>>,
151}
152
153impl DecisionTableOutputField {
154 pub fn write_path(&self) -> (&str, bool) {
155 let field = self.field.trim();
156 match field.strip_suffix("[]") {
157 Some(base) => (base.trim_end(), true),
158 None => (field, false),
159 }
160 }
161}
162
163fn empty_arc_str() -> Arc<str> {
164 Arc::from("")
165}
166
167#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
168#[serde(rename_all = "camelCase")]
169pub struct ExpressionNodeContent {
170 pub expressions: Arc<Vec<Expression>>,
171 #[serde(flatten)]
172 pub transform_attributes: TransformAttributes,
173}
174
175#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
176#[serde(rename_all = "camelCase")]
177pub struct Expression {
178 pub id: Arc<str>,
179 pub key: Arc<str>,
180 pub value: Arc<str>,
181}
182
183#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
184#[serde(rename_all = "camelCase")]
185pub struct SwitchNodeContent {
186 #[serde(default)]
187 pub hit_policy: SwitchStatementHitPolicy,
188 pub statements: Arc<Vec<SwitchStatement>>,
189}
190
191#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
192#[serde(rename_all = "camelCase")]
193pub struct SwitchStatement {
194 pub id: Arc<str>,
195 pub condition: Arc<str>,
196}
197
198#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Default)]
199#[serde(rename_all = "camelCase")]
200pub enum SwitchStatementHitPolicy {
201 #[default]
202 First,
203 Collect,
204}
205
206#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Default)]
207#[serde(rename_all = "camelCase")]
208pub struct TransformAttributes {
209 #[serde(default, deserialize_with = "empty_string_is_none")]
210 pub input_field: Option<Arc<str>>,
211 #[serde(default, deserialize_with = "empty_string_is_none")]
212 pub output_path: Option<Arc<str>>,
213 #[serde(default)]
214 pub execution_mode: TransformExecutionMode,
215 #[serde(default)]
216 pub pass_through: bool,
217}
218
219#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Default)]
220#[serde(rename_all = "camelCase")]
221pub enum TransformExecutionMode {
222 #[default]
223 Single,
224 Loop,
225}
226
227#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Default)]
228#[serde(rename_all = "camelCase")]
229pub struct CustomNodeContent {
230 pub kind: Arc<str>,
231 pub config: Arc<Value>,
232}
233
234fn empty_string_is_none<'de, D>(deserializer: D) -> Result<Option<Arc<str>>, D::Error>
235where
236 D: Deserializer<'de>,
237{
238 #[derive(Deserialize)]
239 #[serde(untagged)]
240 enum StringOrNull {
241 String(Arc<str>),
242 Null,
243 }
244
245 match StringOrNull::deserialize(deserializer)? {
246 StringOrNull::String(s) if s.trim().is_empty() => Ok(None),
247 StringOrNull::String(s) => Ok(Some(s)),
248 StringOrNull::Null => Ok(None),
249 }
250}
251
252fn empty_value_string_is_none_safe<'de, D>(deserializer: D) -> Result<Option<Arc<Value>>, D::Error>
253where
254 D: Deserializer<'de>,
255{
256 let s = empty_string_is_none(deserializer)?;
257 let Some(data) = s else {
258 return Ok(None);
259 };
260
261 Ok(serde_json::from_str(data.as_ref()).ok())
262}
263
264fn deserialize_trim_rules<'de, D>(
265 deserializer: D,
266) -> Result<Arc<Vec<HashMap<Arc<str>, Arc<str>>>>, D::Error>
267where
268 D: Deserializer<'de>,
269{
270 let rules: Vec<HashMap<Arc<str>, Arc<str>>> = Vec::deserialize(deserializer)?;
271
272 let filtered_rules: Vec<HashMap<Arc<str>, Arc<str>>> = rules
273 .into_iter()
274 .map(|rule| {
275 rule.into_iter()
276 .map(|(k, v)| (k, Arc::from(v.trim())))
277 .collect()
278 })
279 .collect();
280
281 Ok(Arc::new(filtered_rules))
282}