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 DecisionContent {
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, PartialEq, Deserialize, Serialize)]
120#[serde(rename_all = "camelCase")]
121pub enum DecisionTableHitPolicy {
122 First,
123 Collect,
124}
125
126#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
127#[serde(rename_all = "camelCase")]
128pub struct DecisionTableInputField {
129 pub id: Arc<str>,
130 pub name: Arc<str>,
131 #[serde(default, deserialize_with = "empty_string_is_none")]
132 pub field: Option<Arc<str>>,
133}
134
135#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
136#[serde(rename_all = "camelCase")]
137pub struct DecisionTableOutputField {
138 pub id: Arc<str>,
139 pub name: Arc<str>,
140 pub field: Arc<str>,
141}
142
143#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
144#[serde(rename_all = "camelCase")]
145pub struct ExpressionNodeContent {
146 pub expressions: Arc<Vec<Expression>>,
147 #[serde(flatten)]
148 pub transform_attributes: TransformAttributes,
149}
150
151#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
152#[serde(rename_all = "camelCase")]
153pub struct Expression {
154 pub id: Arc<str>,
155 pub key: Arc<str>,
156 pub value: Arc<str>,
157}
158
159#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
160#[serde(rename_all = "camelCase")]
161pub struct SwitchNodeContent {
162 #[serde(default)]
163 pub hit_policy: SwitchStatementHitPolicy,
164 pub statements: Arc<Vec<SwitchStatement>>,
165}
166
167#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
168#[serde(rename_all = "camelCase")]
169pub struct SwitchStatement {
170 pub id: Arc<str>,
171 pub condition: Arc<str>,
172}
173
174#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Default)]
175#[serde(rename_all = "camelCase")]
176pub enum SwitchStatementHitPolicy {
177 #[default]
178 First,
179 Collect,
180}
181
182#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Default)]
183#[serde(rename_all = "camelCase")]
184pub struct TransformAttributes {
185 #[serde(default, deserialize_with = "empty_string_is_none")]
186 pub input_field: Option<Arc<str>>,
187 #[serde(default, deserialize_with = "empty_string_is_none")]
188 pub output_path: Option<Arc<str>>,
189 #[serde(default)]
190 pub execution_mode: TransformExecutionMode,
191 #[serde(default)]
192 pub pass_through: bool,
193}
194
195#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Default)]
196#[serde(rename_all = "camelCase")]
197pub enum TransformExecutionMode {
198 #[default]
199 Single,
200 Loop,
201}
202
203#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Default)]
204#[serde(rename_all = "camelCase")]
205pub struct CustomNodeContent {
206 pub kind: Arc<str>,
207 pub config: Arc<Value>,
208}
209
210fn empty_string_is_none<'de, D>(deserializer: D) -> Result<Option<Arc<str>>, D::Error>
211where
212 D: Deserializer<'de>,
213{
214 #[derive(Deserialize)]
215 #[serde(untagged)]
216 enum StringOrNull {
217 String(Arc<str>),
218 Null,
219 }
220
221 match StringOrNull::deserialize(deserializer)? {
222 StringOrNull::String(s) if s.trim().is_empty() => Ok(None),
223 StringOrNull::String(s) => Ok(Some(s)),
224 StringOrNull::Null => Ok(None),
225 }
226}
227
228fn empty_value_string_is_none_safe<'de, D>(deserializer: D) -> Result<Option<Arc<Value>>, D::Error>
229where
230 D: Deserializer<'de>,
231{
232 let s = empty_string_is_none(deserializer)?;
233 let Some(data) = s else {
234 return Ok(None);
235 };
236
237 Ok(serde_json::from_str(data.as_ref()).ok())
238}
239
240fn deserialize_trim_rules<'de, D>(
241 deserializer: D,
242) -> Result<Arc<Vec<HashMap<Arc<str>, Arc<str>>>>, D::Error>
243where
244 D: Deserializer<'de>,
245{
246 let rules: Vec<HashMap<Arc<str>, Arc<str>>> = Vec::deserialize(deserializer)?;
247
248 let filtered_rules: Vec<HashMap<Arc<str>, Arc<str>>> = rules
249 .into_iter()
250 .map(|rule| {
251 rule.into_iter()
252 .map(|(k, v)| (k, Arc::from(v.trim())))
253 .collect()
254 })
255 .collect();
256
257 Ok(Arc::new(filtered_rules))
258}