1use super::Rollout;
13use super::types::{
14 FailurePolicy, ForEachDef, FunctionRef, GroupDef, Logic, LoopDef, NodeHeader, RejectionPolicy,
15 Status, StepDef, TaskDef, WorkflowDef, WorkflowFailurePolicy, defaults,
16};
17use chrono::{DateTime, Utc};
18use serde::ser::{Serialize, SerializeMap, Serializer};
19use std::convert::Infallible;
20
21struct Rfc3339<'a>(&'a DateTime<Utc>);
23
24impl Serialize for Rfc3339<'_> {
25 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
26 serializer.serialize_str(&self.0.to_rfc3339())
27 }
28}
29
30trait Fields {
34 type Error;
35 fn field<T: Serialize + ?Sized>(
36 &mut self,
37 key: &'static str,
38 value: &T,
39 ) -> Result<(), Self::Error>;
40
41 fn opt<T: Serialize>(
43 &mut self,
44 key: &'static str,
45 value: Option<&T>,
46 ) -> Result<(), Self::Error> {
47 value.map_or(Ok(()), |value| self.field(key, value))
48 }
49
50 fn field_if<T: Serialize + ?Sized>(
52 &mut self,
53 present: bool,
54 key: &'static str,
55 value: &T,
56 ) -> Result<(), Self::Error> {
57 if present {
58 self.field(key, value)
59 } else {
60 Ok(())
61 }
62 }
63}
64
65struct Count(usize);
66
67impl Fields for Count {
68 type Error = Infallible;
69 fn field<T: Serialize + ?Sized>(&mut self, _: &'static str, _: &T) -> Result<(), Infallible> {
70 self.0 += 1;
71 Ok(())
72 }
73}
74
75struct Write<M>(M);
76
77impl<M: SerializeMap> Fields for Write<M> {
78 type Error = M::Error;
79 fn field<T: Serialize + ?Sized>(
80 &mut self,
81 key: &'static str,
82 value: &T,
83 ) -> Result<(), M::Error> {
84 self.0.serialize_entry(key, value)
85 }
86}
87
88trait Node {
90 fn fields<F: Fields>(&self, out: &mut F) -> Result<(), F::Error>;
91}
92
93fn node<N: Node, S: Serializer>(node: &N, serializer: S) -> Result<S::Ok, S::Error> {
94 let mut count = Count(0);
95 let Ok(()) = node.fields(&mut count);
96 let mut out = Write(serializer.serialize_map(Some(count.0))?);
97 node.fields(&mut out)?;
98 out.0.end()
99}
100
101fn names<F: Fields>(header: &NodeHeader, out: &mut F) -> Result<(), F::Error> {
103 out.opt("name", header.name.as_ref())?;
104 out.opt("description", header.description.as_ref())
105}
106
107fn ext<F: Fields>(header: &NodeHeader, out: &mut F) -> Result<(), F::Error> {
109 out.field_if(!header.ext.is_empty(), "ext", &header.ext)
110}
111
112impl Node for WorkflowDef {
113 fn fields<F: Fields>(&self, out: &mut F) -> Result<(), F::Error> {
114 out.field("id", &self.id)?;
115 names(&self.header, out)?;
116 out.field_if(
117 self.priority != defaults::PRIORITY,
118 "priority",
119 &self.priority,
120 )?;
121 out.opt("condition", self.condition.as_ref())?;
122 out.field("tasks", &self.tasks)?;
123 out.field_if(
124 self.on_failure != WorkflowFailurePolicy::default(),
125 "on_failure",
126 &self.on_failure,
127 )?;
128 out.field_if(self.channel != defaults::CHANNEL, "channel", &self.channel)?;
129 out.field_if(self.status != Status::default(), "status", &self.status)?;
130 out.field_if(self.version != defaults::VERSION, "version", &self.version)?;
131 out.opt("rollout", self.rollout.as_ref())?;
132 out.opt("loop", self.r#loop.as_ref())?;
133 out.field_if(!self.tags.is_empty(), "tags", &self.tags)?;
134 if let Some(at) = &self.created_at {
135 out.field("created_at", &Rfc3339(at))?;
136 }
137 if let Some(at) = &self.updated_at {
138 out.field("updated_at", &Rfc3339(at))?;
139 }
140 ext(&self.header, out)
141 }
142}
143
144impl Node for TaskDef {
145 fn fields<F: Fields>(&self, out: &mut F) -> Result<(), F::Error> {
146 out.field("id", &self.id)?;
147 names(&self.header, out)?;
148 out.opt("condition", self.condition.as_ref())?;
149 out.field("function", &self.function)?;
150 out.field_if(
151 self.on_failure != FailurePolicy::default(),
152 "on_failure",
153 &self.on_failure,
154 )?;
155 out.field_if(
156 self.on_rejection != RejectionPolicy::default(),
157 "on_rejection",
158 &self.on_rejection,
159 )?;
160 out.field_if(self.terminal, "terminal", &true)?;
161 out.opt("for_each", self.for_each.as_ref())?;
162 ext(&self.header, out)
163 }
164}
165
166impl Node for GroupDef {
167 fn fields<F: Fields>(&self, out: &mut F) -> Result<(), F::Error> {
168 out.field("id", &self.id)?;
169 names(&self.header, out)?;
170 out.opt("condition", self.condition.as_ref())?;
171 out.field_if(self.terminal, "terminal", &true)?;
172 out.field("tasks", &self.tasks)?;
173 ext(&self.header, out)
174 }
175}
176
177impl Node for LoopDef {
178 fn fields<F: Fields>(&self, out: &mut F) -> Result<(), F::Error> {
179 out.opt("counter", self.counter.as_ref())?;
180 out.field_if(self.init != defaults::INIT, "init", &self.init)?;
181 out.field_if(
182 self.increment != defaults::INCREMENT,
183 "increment",
184 &self.increment,
185 )?;
186 out.field("max", &self.max)?;
187 out.field_if(!self.setup.is_empty(), "setup", &self.setup)?;
188 out.opt("over", self.over.as_ref())?;
189 out.opt("as", self.r#as.as_ref())?;
190 out.opt("scratch", self.scratch.as_ref())
191 }
192}
193
194impl Node for ForEachDef {
195 fn fields<F: Fields>(&self, out: &mut F) -> Result<(), F::Error> {
196 out.field("over", &self.over)?;
197 out.field("as", &self.r#as)?;
198 out.field_if(
199 self.max_concurrency != defaults::MAX_CONCURRENCY,
200 "max_concurrency",
201 &self.max_concurrency,
202 )?;
203 out.opt("collect", self.collect.as_ref())?;
204 out.opt("into", self.into.as_ref())
205 }
206}
207
208impl Node for FunctionRef {
209 fn fields<F: Fields>(&self, out: &mut F) -> Result<(), F::Error> {
210 out.field("name", &self.name)?;
211 out.field("input", &self.input)
212 }
213}
214
215impl Node for Rollout {
216 fn fields<F: Fields>(&self, out: &mut F) -> Result<(), F::Error> {
217 out.field("bucket_start", &self.bucket_start)?;
218 out.field("bucket_end", &self.bucket_end)
219 }
220}
221
222impl Node for NodeHeader {
223 fn fields<F: Fields>(&self, out: &mut F) -> Result<(), F::Error> {
224 names(self, out)?;
225 ext(self, out)
226 }
227}
228
229macro_rules! serialize_as_node {
231 ($($ty:ty),* $(,)?) => {$(
232 impl Serialize for $ty {
233 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
234 node(self, serializer)
235 }
236 }
237 )*};
238}
239
240serialize_as_node!(
241 WorkflowDef,
242 TaskDef,
243 GroupDef,
244 LoopDef,
245 ForEachDef,
246 FunctionRef,
247 Rollout,
248 NodeHeader,
249);
250
251impl Serialize for StepDef {
252 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
253 match self {
254 Self::Task(task) => task.serialize(serializer),
255 Self::Group(group) => group.serialize(serializer),
256 }
257 }
258}
259
260impl Serialize for Logic {
261 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
262 self.0.serialize(serializer)
263 }
264}
265
266macro_rules! serialize_as_spelling {
268 ($($ty:ty),* $(,)?) => {$(
269 impl Serialize for $ty {
270 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
271 serializer.serialize_str(self.as_str())
272 }
273 }
274 )*};
275}
276
277serialize_as_spelling!(
278 Status,
279 FailurePolicy,
280 RejectionPolicy,
281 WorkflowFailurePolicy,
282);