1use crate::models::duration::*;
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4use std::collections::HashMap;
5
6#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
8pub struct RetryPolicyDefinition {
9 #[serde(skip_serializing_if = "Option::is_none")]
11 pub when: Option<String>,
12
13 #[serde(rename = "exceptWhen", skip_serializing_if = "Option::is_none")]
15 pub except_when: Option<String>,
16
17 #[serde(skip_serializing_if = "Option::is_none")]
19 pub limit: Option<RetryPolicyLimitDefinition>,
20
21 #[serde(skip_serializing_if = "Option::is_none")]
23 pub delay: Option<OneOfDurationOrIso8601Expression>,
24
25 #[serde(skip_serializing_if = "Option::is_none")]
27 pub backoff: Option<BackoffStrategyDefinition>,
28
29 #[serde(skip_serializing_if = "Option::is_none")]
31 pub jitter: Option<JitterDefinition>,
32}
33
34#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
36pub struct RetryPolicyLimitDefinition {
37 #[serde(skip_serializing_if = "Option::is_none")]
39 pub attempt: Option<RetryAttemptLimitDefinition>,
40
41 #[serde(skip_serializing_if = "Option::is_none")]
43 pub duration: Option<OneOfDurationOrIso8601Expression>,
44}
45
46#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
48pub struct RetryAttemptLimitDefinition {
49 #[serde(skip_serializing_if = "Option::is_none")]
51 pub count: Option<u16>,
52
53 #[serde(skip_serializing_if = "Option::is_none")]
55 pub duration: Option<OneOfDurationOrIso8601Expression>,
56}
57
58#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
60pub struct BackoffStrategyDefinition {
61 #[serde(skip_serializing_if = "Option::is_none")]
63 pub constant: Option<ConstantBackoffDefinition>,
64
65 #[serde(skip_serializing_if = "Option::is_none")]
67 pub exponential: Option<ExponentialBackoffDefinition>,
68
69 #[serde(skip_serializing_if = "Option::is_none")]
71 pub linear: Option<LinearBackoffDefinition>,
72}
73
74#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
76pub struct ConstantBackoffDefinition {
77 #[serde(flatten, skip_serializing_if = "Option::is_none")]
79 pub definition: Option<HashMap<String, Value>>,
80}
81impl ConstantBackoffDefinition {
82 pub fn with_delay(delay: &str) -> Self {
84 let mut map = HashMap::new();
85 map.insert("delay".to_string(), Value::String(delay.to_string()));
86 Self {
87 definition: Some(map),
88 }
89 }
90
91 pub fn delay(&self) -> Option<&str> {
93 self.definition.as_ref()?.get("delay")?.as_str()
94 }
95}
96
97#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
99pub struct ExponentialBackoffDefinition {
100 #[serde(flatten, skip_serializing_if = "Option::is_none")]
102 pub definition: Option<HashMap<String, Value>>,
103}
104impl ExponentialBackoffDefinition {
105 pub fn with_factor(factor: f64) -> Self {
107 let mut map = HashMap::new();
108 map.insert("factor".to_string(), Value::from(factor));
109 Self {
110 definition: Some(map),
111 }
112 }
113
114 pub fn with_factor_and_max_delay(factor: f64, max_delay: &str) -> Self {
116 let mut map = HashMap::new();
117 map.insert("factor".to_string(), Value::from(factor));
118 map.insert("maxDelay".to_string(), Value::String(max_delay.to_string()));
119 Self {
120 definition: Some(map),
121 }
122 }
123
124 pub fn factor(&self) -> Option<f64> {
126 self.definition.as_ref()?.get("factor")?.as_f64()
127 }
128
129 pub fn max_delay(&self) -> Option<&str> {
131 self.definition.as_ref()?.get("maxDelay")?.as_str()
132 }
133}
134
135#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
137pub struct LinearBackoffDefinition {
138 #[serde(skip_serializing_if = "Option::is_none")]
140 pub increment: Option<Duration>,
141
142 #[serde(flatten, skip_serializing_if = "Option::is_none")]
144 pub definition: Option<HashMap<String, Value>>,
145}
146
147#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
149pub struct JitterDefinition {
150 pub from: Duration,
152
153 pub to: Duration,
155}
156
157define_one_of_or_reference!(
158 OneOfRetryPolicyDefinitionOrReference, Retry(Box<RetryPolicyDefinition>)
160);
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165
166 #[test]
167 fn test_retry_policy_serialize() {
168 let policy = RetryPolicyDefinition {
169 when: Some("${someCondition}".to_string()),
170 except_when: Some("${someOtherCondition}".to_string()),
171 delay: Some(OneOfDurationOrIso8601Expression::Duration(
172 Duration::from_seconds(5),
173 )),
174 backoff: Some(BackoffStrategyDefinition {
175 exponential: Some(ExponentialBackoffDefinition::with_factor(2.0)),
176 constant: None,
177 linear: None,
178 }),
179 limit: Some(RetryPolicyLimitDefinition {
180 attempt: Some(RetryAttemptLimitDefinition {
181 count: Some(3),
182 duration: Some(OneOfDurationOrIso8601Expression::Duration(
183 Duration::from_minutes(1),
184 )),
185 }),
186 duration: Some(OneOfDurationOrIso8601Expression::Duration(
187 Duration::from_minutes(10),
188 )),
189 }),
190 jitter: Some(JitterDefinition {
191 from: Duration::from_seconds(1),
192 to: Duration::from_seconds(3),
193 }),
194 };
195
196 let json = serde_json::to_string(&policy).unwrap();
197 assert!(json.contains("\"when\":\"${someCondition}\""));
198 assert!(json.contains("\"exceptWhen\":\"${someOtherCondition}\""));
199 assert!(json.contains("\"factor\":2"));
200 assert!(json.contains("\"count\":3"));
201 }
202
203 #[test]
204 fn test_retry_policy_deserialize_inline() {
205 let json = r#"{
206 "when": "${someCondition}",
207 "exceptWhen": "${someOtherCondition}",
208 "delay": {"seconds": 5},
209 "backoff": {"exponential": {"factor": 2}},
210 "limit": {
211 "attempt": {"count": 3, "duration": {"minutes": 1}},
212 "duration": {"minutes": 10}
213 },
214 "jitter": {"from": {"seconds": 1}, "to": {"seconds": 3}}
215 }"#;
216
217 let policy: RetryPolicyDefinition = serde_json::from_str(json).unwrap();
218 assert_eq!(policy.when, Some("${someCondition}".to_string()));
219 assert_eq!(
220 policy.except_when,
221 Some("${someOtherCondition}".to_string())
222 );
223 assert!(policy.delay.is_some());
224 assert!(policy.backoff.is_some());
225 assert!(policy.backoff.as_ref().unwrap().exponential.is_some());
226 assert_eq!(
227 policy
228 .backoff
229 .as_ref()
230 .unwrap()
231 .exponential
232 .as_ref()
233 .unwrap()
234 .factor(),
235 Some(2.0)
236 );
237 assert!(policy.limit.is_some());
238 assert_eq!(
239 policy
240 .limit
241 .as_ref()
242 .unwrap()
243 .attempt
244 .as_ref()
245 .unwrap()
246 .count,
247 Some(3)
248 );
249 assert!(policy.jitter.is_some());
250 }
251
252 #[test]
253 fn test_oneof_retry_reference_deserialize() {
254 let json = r#""default""#;
255 let oneof: OneOfRetryPolicyDefinitionOrReference = serde_json::from_str(json).unwrap();
256 match oneof {
257 OneOfRetryPolicyDefinitionOrReference::Reference(name) => {
258 assert_eq!(name, "default");
259 }
260 _ => panic!("Expected Reference variant"),
261 }
262 }
263
264 #[test]
265 fn test_oneof_retry_inline_deserialize() {
266 let json = r#"{"delay": {"seconds": 3}, "limit": {"attempt": {"count": 5}}}"#;
267 let oneof: OneOfRetryPolicyDefinitionOrReference = serde_json::from_str(json).unwrap();
268 match oneof {
269 OneOfRetryPolicyDefinitionOrReference::Retry(policy) => {
270 assert!(policy.delay.is_some());
271 assert_eq!(
272 policy
273 .limit
274 .as_ref()
275 .unwrap()
276 .attempt
277 .as_ref()
278 .unwrap()
279 .count,
280 Some(5)
281 );
282 }
283 _ => panic!("Expected Retry variant"),
284 }
285 }
286
287 #[test]
288 fn test_constant_backoff_definition() {
289 let constant = ConstantBackoffDefinition::with_delay("PT5S");
290 assert_eq!(constant.delay(), Some("PT5S"));
291
292 let empty = ConstantBackoffDefinition::default();
293 assert_eq!(empty.delay(), None);
294 }
295
296 #[test]
297 fn test_exponential_backoff_definition() {
298 let exp = ExponentialBackoffDefinition::with_factor(2.5);
299 assert_eq!(exp.factor(), Some(2.5));
300 assert_eq!(exp.max_delay(), None);
301
302 let exp_with_max = ExponentialBackoffDefinition::with_factor_and_max_delay(2.0, "PT30S");
303 assert_eq!(exp_with_max.factor(), Some(2.0));
304 assert_eq!(exp_with_max.max_delay(), Some("PT30S"));
305 }
306
307 #[test]
308 fn test_backoff_strategy_serialize() {
309 let backoff = BackoffStrategyDefinition {
310 constant: None,
311 exponential: Some(ExponentialBackoffDefinition::with_factor(2.0)),
312 linear: Some(LinearBackoffDefinition {
313 increment: Some(Duration::from_milliseconds(500)),
314 definition: None,
315 }),
316 };
317
318 let json = serde_json::to_string(&backoff).unwrap();
319 assert!(json.contains("\"exponential\""));
320 assert!(json.contains("\"factor\":2"));
321 assert!(json.contains("\"linear\""));
322 assert!(json.contains("\"increment\""));
323 }
324
325 #[test]
326 fn test_backoff_strategy_deserialize() {
327 let json = r#"{"exponential": {"factor": 2.5, "maxDelay": "PT30S"}}"#;
328 let backoff: BackoffStrategyDefinition = serde_json::from_str(json).unwrap();
329 assert!(backoff.exponential.is_some());
330 assert_eq!(backoff.exponential.as_ref().unwrap().factor(), Some(2.5));
331 assert_eq!(
332 backoff.exponential.as_ref().unwrap().max_delay(),
333 Some("PT30S")
334 );
335 }
336
337 #[test]
338 fn test_retry_attempt_limit_default() {
339 let limit = RetryAttemptLimitDefinition::default();
340 assert_eq!(limit.count, None);
341 assert_eq!(limit.duration, None);
342 }
343
344 #[test]
345 fn test_jitter_definition() {
346 let jitter = JitterDefinition {
347 from: Duration::from_seconds(1),
348 to: Duration::from_seconds(3),
349 };
350 assert_eq!(jitter.from.total_milliseconds(), 1000);
351 assert_eq!(jitter.to.total_milliseconds(), 3000);
352 }
353
354 #[test]
357 fn test_retry_policy_roundtrip_with_iso8601() {
358 let json = r#"{
360 "when": "${someCondition}",
361 "exceptWhen": "${someOtherCondition}",
362 "delay": {"seconds": 5},
363 "backoff": {"exponential": {"factor": 2}},
364 "limit": {
365 "attempt": {"count": 3, "duration": {"minutes": 1}},
366 "duration": {"minutes": 10}
367 },
368 "jitter": {"from": {"seconds": 1}, "to": {"seconds": 3}}
369 }"#;
370 let policy: RetryPolicyDefinition = serde_json::from_str(json).unwrap();
371 assert_eq!(policy.when, Some("${someCondition}".to_string()));
372 assert_eq!(
373 policy.except_when,
374 Some("${someOtherCondition}".to_string())
375 );
376 assert!(policy.delay.is_some());
377 assert!(policy.delay.as_ref().unwrap().is_duration());
378 match policy.delay.as_ref().unwrap() {
379 OneOfDurationOrIso8601Expression::Duration(d) => assert_eq!(d.seconds, Some(5)),
380 _ => panic!("Expected Duration variant"),
381 }
382 let serialized = serde_json::to_string(&policy).unwrap();
384 let deserialized: RetryPolicyDefinition = serde_json::from_str(&serialized).unwrap();
385 assert_eq!(policy, deserialized);
386 }
387
388 #[test]
389 fn test_retry_policy_constant_backoff_roundtrip() {
390 let json = r#"{
391 "delay": {"seconds": 3},
392 "backoff": {"constant": {"delay": "PT5S"}},
393 "limit": {"attempt": {"count": 5}}
394 }"#;
395 let policy: RetryPolicyDefinition = serde_json::from_str(json).unwrap();
396 assert!(policy.backoff.as_ref().unwrap().constant.is_some());
397 let serialized = serde_json::to_string(&policy).unwrap();
398 let deserialized: RetryPolicyDefinition = serde_json::from_str(&serialized).unwrap();
399 assert_eq!(policy, deserialized);
400 }
401
402 #[test]
403 fn test_retry_policy_linear_backoff_deserialize() {
404 let json = r#"{
405 "delay": {"seconds": 1},
406 "backoff": {"linear": {"increment": {"seconds": 2}}},
407 "limit": {"attempt": {"count": 3}}
408 }"#;
409 let policy: RetryPolicyDefinition = serde_json::from_str(json).unwrap();
410 assert!(policy.backoff.as_ref().unwrap().linear.is_some());
411 let linear = policy.backoff.as_ref().unwrap().linear.as_ref().unwrap();
412 assert!(linear.increment.is_some());
413 }
414
415 #[test]
416 fn test_oneof_retry_reference_roundtrip() {
417 let ref_val = OneOfRetryPolicyDefinitionOrReference::Reference("default".to_string());
418 let serialized = serde_json::to_string(&ref_val).unwrap();
419 assert_eq!(serialized, r#""default""#);
420 let deserialized: OneOfRetryPolicyDefinitionOrReference =
421 serde_json::from_str(&serialized).unwrap();
422 assert_eq!(ref_val, deserialized);
423 }
424
425 #[test]
426 fn test_oneof_retry_inline_roundtrip() {
427 let json = r#"{"delay": {"seconds": 3}, "backoff": {"exponential": {}}, "limit": {"attempt": {"count": 5}}}"#;
428 let oneof: OneOfRetryPolicyDefinitionOrReference = serde_json::from_str(json).unwrap();
429 match &oneof {
430 OneOfRetryPolicyDefinitionOrReference::Retry(policy) => {
431 assert!(policy.delay.is_some());
432 assert!(policy.backoff.is_some());
433 }
434 _ => panic!("Expected Retry variant"),
435 }
436 let serialized = serde_json::to_string(&oneof).unwrap();
437 let deserialized: OneOfRetryPolicyDefinitionOrReference =
438 serde_json::from_str(&serialized).unwrap();
439 assert_eq!(oneof, deserialized);
440 }
441
442 #[test]
443 fn test_retry_limit_duration_only() {
444 let json = r#"{"limit": {"duration": {"minutes": 5}}}"#;
445 let policy: RetryPolicyDefinition = serde_json::from_str(json).unwrap();
446 assert!(policy.limit.is_some());
447 assert!(policy.limit.as_ref().unwrap().duration.is_some());
448 assert!(policy.limit.as_ref().unwrap().attempt.is_none());
449 }
450
451 #[test]
452 fn test_retry_policy_minimal() {
453 let json = r#"{"delay": {"seconds": 1}}"#;
455 let policy: RetryPolicyDefinition = serde_json::from_str(json).unwrap();
456 assert!(policy.delay.is_some());
457 assert!(policy.when.is_none());
458 assert!(policy.except_when.is_none());
459 assert!(policy.backoff.is_none());
460 assert!(policy.limit.is_none());
461 assert!(policy.jitter.is_none());
462 }
463
464 #[test]
465 fn test_retry_policy_default() {
466 let policy = RetryPolicyDefinition::default();
467 assert!(policy.when.is_none());
468 assert!(policy.except_when.is_none());
469 assert!(policy.delay.is_none());
470 assert!(policy.backoff.is_none());
471 assert!(policy.limit.is_none());
472 assert!(policy.jitter.is_none());
473 }
474}