Skip to main content

camel_dsl/
model.rs

1pub use camel_api::{LanguageExpressionDef, StreamSplitConfig, ValueSourceDef};
2
3#[derive(Default)]
4pub struct SecurityCompileContext {
5    pub authenticator: Option<std::sync::Arc<dyn camel_auth::TokenAuthenticator>>,
6    pub registry: Option<std::sync::Arc<camel_auth::SecurityPolicyRegistry>>,
7    pub evaluator_registry: Option<std::sync::Arc<camel_auth::PermissionEvaluatorRegistry>>,
8}
9
10impl Clone for SecurityCompileContext {
11    fn clone(&self) -> Self {
12        Self {
13            authenticator: self.authenticator.clone(),
14            registry: self.registry.clone(),
15            evaluator_registry: self.evaluator_registry.clone(),
16        }
17    }
18}
19
20impl SecurityCompileContext {
21    pub fn new(
22        authenticator: Option<std::sync::Arc<dyn camel_auth::TokenAuthenticator>>,
23        registry: Option<std::sync::Arc<camel_auth::SecurityPolicyRegistry>>,
24    ) -> Self {
25        Self {
26            authenticator,
27            registry,
28            evaluator_registry: None,
29        }
30    }
31
32    pub fn with_evaluator_registry(
33        mut self,
34        registry: std::sync::Arc<camel_auth::PermissionEvaluatorRegistry>,
35    ) -> Self {
36        self.evaluator_registry = Some(registry);
37        self
38    }
39
40    pub fn with_security_policy_registry(
41        mut self,
42        registry: std::sync::Arc<camel_auth::SecurityPolicyRegistry>,
43    ) -> Self {
44        self.registry = Some(registry);
45        self
46    }
47}
48
49#[derive(Debug, Clone, PartialEq, Eq)]
50pub enum DeclarativeConcurrency {
51    Sequential,
52    Concurrent { max: Option<usize> },
53}
54
55#[derive(Debug, Clone, PartialEq, Eq)]
56pub struct DeclarativeCircuitBreaker {
57    pub failure_threshold: u32,
58    pub open_duration_ms: u64,
59}
60
61#[derive(Debug, Clone, PartialEq)]
62pub enum DeclarativeSecurityPolicy {
63    Roles {
64        roles: Vec<String>,
65        all_required: bool,
66        trust_upstream_principal: bool,
67    },
68    Scopes {
69        scopes: Vec<String>,
70        all_required: bool,
71        trust_upstream_principal: bool,
72    },
73    Ref {
74        name: String,
75    },
76    /// WASM security policy reference. The `path` field is the registry name
77    /// of a policy registered via `[security.policies.wasm.<name>]` in Camel.toml.
78    /// Per-route `config` is not supported (registry is instance-based, not
79    /// factory-based โ€” see ADR-0014 ยง4 closure bd rc-0te).
80    Wasm {
81        /// Registry name of the WASM policy (from `[security.policies.wasm.<name>]` in Camel.toml).
82        path: String,
83        /// Reserved โ€” must be empty. Use Camel.toml `[security.policies.wasm.<name>.config]` instead.
84        config: std::collections::HashMap<String, String>,
85    },
86    Permission {
87        policy: String,
88        resource: camel_auth::PermissionValueSource,
89        action: camel_auth::PermissionValueSource,
90        scopes: Vec<String>,
91        context: camel_auth::PermissionContextConfig,
92        cache_ttl_secs: Option<u64>,
93        cache_negative_ttl_secs: Option<u64>,
94    },
95}
96
97#[derive(Debug, Clone, PartialEq)]
98pub struct DeclarativeRedeliveryPolicy {
99    pub max_attempts: u32,
100    pub initial_delay_ms: u64,
101    pub multiplier: f64,
102    pub max_delay_ms: u64,
103    pub jitter_factor: f64,
104    pub handled_by: Option<String>,
105}
106
107#[derive(Debug, Clone, PartialEq)]
108pub struct DeclarativeOnException {
109    pub kind: Option<String>,
110    pub message_contains: Option<String>,
111    pub retry: Option<DeclarativeRedeliveryPolicy>,
112    pub steps: Vec<DeclarativeStep>,
113    pub handled: Option<bool>,
114    pub continued: Option<bool>,
115}
116
117#[derive(Debug, Clone, PartialEq, Default)]
118pub struct DeclarativeErrorHandler {
119    pub dead_letter_channel: Option<String>,
120    pub retry: Option<DeclarativeRedeliveryPolicy>,
121    pub on_exceptions: Option<Vec<DeclarativeOnException>>,
122    pub use_original_message: bool,
123}
124
125#[derive(Debug, Clone)]
126pub struct DeclarativeRoute {
127    pub from: String,
128    pub route_id: String,
129    pub auto_startup: bool,
130    pub startup_order: i32,
131    pub concurrency: Option<DeclarativeConcurrency>,
132    pub error_handler: Option<DeclarativeErrorHandler>,
133    pub circuit_breaker: Option<DeclarativeCircuitBreaker>,
134    pub security_policy: Option<DeclarativeSecurityPolicy>,
135    pub unit_of_work: Option<camel_api::UnitOfWorkConfig>,
136    pub steps: Vec<DeclarativeStep>,
137}
138
139#[derive(Debug, Clone, PartialEq, Eq)]
140pub struct ToStepDef {
141    pub uri: String,
142}
143
144impl ToStepDef {
145    pub fn new(uri: impl Into<String>) -> Self {
146        Self { uri: uri.into() }
147    }
148}
149
150#[derive(Debug, Clone, PartialEq, Eq)]
151pub enum LogLevelDef {
152    Trace,
153    Debug,
154    Info,
155    Warn,
156    Error,
157}
158
159// Note: `Eq` is not derived because `ValueSourceDef` contains `serde_json::Value`
160// which does not implement `Eq` (due to floating-point fields).
161#[derive(Debug, Clone, PartialEq)]
162pub struct LogStepDef {
163    pub message: ValueSourceDef,
164    pub level: LogLevelDef,
165}
166
167impl LogStepDef {
168    pub fn info(message: impl Into<String>) -> Self {
169        Self {
170            message: ValueSourceDef::Literal(serde_json::Value::String(message.into())),
171            level: LogLevelDef::Info,
172        }
173    }
174}
175
176#[derive(Debug, Clone, PartialEq)]
177pub struct SetHeaderStepDef {
178    pub key: String,
179    pub value: ValueSourceDef,
180}
181
182impl SetHeaderStepDef {
183    pub fn literal(key: impl Into<String>, value: impl Into<String>) -> Self {
184        Self {
185            key: key.into(),
186            value: ValueSourceDef::Literal(serde_json::Value::String(value.into())),
187        }
188    }
189}
190
191#[derive(Debug, Clone, PartialEq)]
192pub struct SetPropertyStepDef {
193    pub key: String,
194    pub value: ValueSourceDef,
195}
196
197impl SetPropertyStepDef {
198    pub fn literal(key: impl Into<String>, value: impl Into<String>) -> Self {
199        Self {
200            key: key.into(),
201            value: ValueSourceDef::Literal(serde_json::Value::String(value.into())),
202        }
203    }
204}
205
206#[derive(Debug, Clone, PartialEq)]
207pub struct SetBodyStepDef {
208    pub value: ValueSourceDef,
209}
210
211#[derive(Debug, Clone, PartialEq)]
212pub struct FilterStepDef {
213    pub predicate: LanguageExpressionDef,
214    pub steps: Vec<DeclarativeStep>,
215}
216
217#[derive(Debug, Clone, PartialEq, Eq)]
218pub struct FunctionStepDef {
219    pub runtime: String,
220    pub source: String,
221    pub timeout_ms: Option<u64>,
222}
223
224#[derive(Debug, Clone, PartialEq)]
225pub struct WhenStepDef {
226    pub predicate: LanguageExpressionDef,
227    pub steps: Vec<DeclarativeStep>,
228}
229
230#[derive(Debug, Clone, PartialEq)]
231pub struct ChoiceStepDef {
232    pub whens: Vec<WhenStepDef>,
233    pub otherwise: Option<Vec<DeclarativeStep>>,
234}
235
236#[derive(Debug, Clone, PartialEq)]
237pub enum SplitExpressionDef {
238    BodyLines,
239    BodyJsonArray,
240    Language(LanguageExpressionDef),
241    Stream(StreamSplitConfig),
242}
243
244#[derive(Debug, Clone, PartialEq, Eq)]
245pub enum SplitAggregationDef {
246    LastWins,
247    CollectAll,
248    Original,
249}
250
251#[derive(Debug, Clone, PartialEq)]
252pub struct SplitStepDef {
253    pub expression: SplitExpressionDef,
254    pub aggregation: SplitAggregationDef,
255    pub parallel: bool,
256    pub parallel_limit: Option<usize>,
257    pub stop_on_exception: bool,
258    pub steps: Vec<DeclarativeStep>,
259}
260
261#[derive(Debug, Clone, PartialEq, Eq)]
262pub enum AggregateStrategyDef {
263    CollectAll,
264}
265
266#[derive(Debug, Clone, PartialEq)]
267pub struct AggregateStepDef {
268    pub header: String,
269    pub correlation_key: Option<String>,
270    pub completion_size: Option<usize>,
271    pub completion_timeout_ms: Option<u64>,
272    pub completion_predicate: Option<LanguageExpressionDef>,
273    pub strategy: AggregateStrategyDef,
274    pub max_buckets: Option<usize>,
275    pub bucket_ttl_ms: Option<u64>,
276    pub force_completion_on_stop: Option<bool>,
277    pub discard_on_timeout: Option<bool>,
278}
279
280#[derive(Debug, Clone, PartialEq, Eq)]
281pub struct WireTapStepDef {
282    pub uri: String,
283}
284
285#[derive(Debug, Clone, PartialEq, Eq)]
286pub struct BeanStepDef {
287    pub name: String,
288    pub method: String,
289}
290
291impl BeanStepDef {
292    pub fn new(name: impl Into<String>, method: impl Into<String>) -> Self {
293        Self {
294            name: name.into(),
295            method: method.into(),
296        }
297    }
298}
299
300#[derive(Debug, Clone, PartialEq, Eq, Default)]
301pub enum ThrottleStrategyDef {
302    #[default]
303    Delay,
304    Reject,
305    Drop,
306}
307
308#[derive(Debug, Clone, PartialEq)]
309pub struct ThrottleStepDef {
310    pub max_requests: usize,
311    pub period_ms: u64,
312    pub strategy: ThrottleStrategyDef,
313    pub steps: Vec<DeclarativeStep>,
314}
315
316#[derive(Debug, Clone, PartialEq, Eq, Default)]
317pub enum LoadBalanceStrategyDef {
318    #[default]
319    RoundRobin,
320    Random,
321    Failover,
322    Weighted {
323        distribution_ratio: String,
324    },
325}
326
327#[derive(Debug, Clone, PartialEq)]
328pub struct LoadBalanceStepDef {
329    pub strategy: LoadBalanceStrategyDef,
330    pub steps: Vec<DeclarativeStep>,
331}
332
333#[derive(Debug, Clone, PartialEq, Eq)]
334pub struct DynamicRouterStepDef {
335    pub expression: LanguageExpressionDef,
336    pub uri_delimiter: String,
337    pub cache_size: i32,
338    pub ignore_invalid_endpoints: bool,
339    pub max_iterations: usize,
340}
341
342#[derive(Debug, Clone, PartialEq, Eq)]
343pub struct RoutingSlipStepDef {
344    pub expression: LanguageExpressionDef,
345    pub uri_delimiter: String,
346    pub cache_size: i32,
347    pub ignore_invalid_endpoints: bool,
348}
349
350#[derive(Debug, Clone, PartialEq, Eq)]
351pub struct RecipientListStepDef {
352    pub expression: LanguageExpressionDef,
353    pub delimiter: String,
354    pub parallel: bool,
355    pub parallel_limit: Option<usize>,
356    pub stop_on_exception: bool,
357    pub aggregation: MulticastAggregationDef,
358}
359
360#[derive(Debug, Clone, PartialEq, Eq)]
361pub enum MulticastAggregationDef {
362    LastWins,
363    CollectAll,
364    Original,
365}
366
367#[derive(Debug, Clone, PartialEq)]
368pub struct MulticastStepDef {
369    pub steps: Vec<DeclarativeStep>,
370    pub parallel: bool,
371    pub parallel_limit: Option<usize>,
372    pub stop_on_exception: bool,
373    pub timeout_ms: Option<u64>,
374    pub aggregation: MulticastAggregationDef,
375}
376
377#[derive(Debug, Clone, PartialEq, Eq)]
378pub struct ScriptStepDef {
379    pub expression: LanguageExpressionDef,
380}
381
382#[derive(Debug, Clone, PartialEq, Eq)]
383pub enum BodyTypeDef {
384    Text,
385    Json,
386    Bytes,
387    Xml,
388    Empty,
389}
390
391#[derive(Debug, Clone, PartialEq)]
392pub struct DataFormatDef {
393    pub format: String,
394    /// Optional JSON Schema for request-body validation (REST DSL
395    /// `request_schema`). When present, the compiled UnmarshalService is
396    /// wrapped with a `JsonSchemaValidateService`.
397    pub schema: Option<serde_json::Value>,
398    /// Optional per-format configuration (e.g. `{ "max_bytes": 67108864 }`).
399    /// Deserialized by the config-aware factory per ADR-0038.
400    pub config: Option<serde_json::Value>,
401}
402
403#[derive(Debug, Clone, PartialEq, Eq)]
404pub struct DelayStepDef {
405    pub delay_ms: u64,
406    pub dynamic_header: Option<String>,
407}
408
409#[derive(Debug, Clone, PartialEq)]
410pub struct LoopStepDef {
411    pub count: Option<usize>,
412    pub while_predicate: Option<LanguageExpressionDef>,
413    pub steps: Vec<DeclarativeStep>,
414    pub max_iterations: Option<usize>,
415}
416
417#[derive(Debug, Clone, PartialEq, Eq)]
418pub struct StreamCacheStepDef {
419    pub threshold: Option<usize>,
420}
421
422#[derive(Debug, Clone, PartialEq, Eq)]
423pub struct ValidateStepDef {
424    pub predicate: LanguageExpressionDef,
425}
426
427/// Claim Check EIP step definition.
428///
429/// Stashes/retrieves the message body from a `ClaimCheckRepository` by key.
430#[derive(Debug, Clone, PartialEq, Eq)]
431pub struct ClaimCheckStepDef {
432    /// Name of the registered `ClaimCheckRepository` (e.g. `"memory"`).
433    pub repository: String,
434    /// Operation: "set", "get", "get_and_remove", "push", "pop".
435    pub operation: String,
436    /// Expression that extracts the claim-check key from the exchange.
437    pub key: LanguageExpressionDef,
438    /// Optional filter string for selective merge-back during checkout operations.
439    pub filter: Option<String>,
440}
441
442/// Idempotent Consumer EIP step definition.
443///
444/// Wraps a child sub-pipeline that runs only when the exchange's message-id
445/// is NOT already present in the named `repository`. See ADR-0023.
446#[derive(Debug, Clone, PartialEq)]
447pub struct IdempotentConsumerStepDef {
448    /// Name of the registered `IdempotentRepository` (e.g. `"memory"`).
449    pub repository: String,
450    /// Expression that extracts the message-id key from the exchange.
451    pub expression: LanguageExpressionDef,
452    /// Child sub-pipeline executed on first-time (non-duplicate) exchanges.
453    pub steps: Vec<DeclarativeStep>,
454    /// If `true`, reserve the key in the repository BEFORE running the child
455    /// (eager mode). Default `false` (lazy: add only after the child completes).
456    pub eager: Option<bool>,
457    /// If `true` and `eager` is `true`, remove the key from the repository
458    /// when the child returns `Failed`. Default `false`.
459    pub remove_on_failure: Option<bool>,
460}
461
462/// Cache EIP step definition.
463///
464/// Looks up/puts data in a `CacheRepository` by key, running `on_miss`
465/// when the key is absent.
466#[derive(Debug, Clone, PartialEq)]
467pub struct CacheStepDef {
468    /// Name of the registered `CacheRepository` (optional; defaults to system default).
469    pub repository: Option<String>,
470    /// Expression that extracts the cache key from the exchange.
471    pub key: LanguageExpressionDef,
472    /// Optional TTL as a string expression (e.g. "60s", "5m").
473    pub ttl: Option<String>,
474    /// Maximum bytes per cache entry.
475    pub max_entry_bytes: Option<usize>,
476    /// Child sub-pipeline executed on cache miss.
477    pub on_miss: Vec<DeclarativeStep>,
478}
479
480/// Cache Invalidate EIP step definition.
481///
482/// Removes an entry from a `CacheRepository` by key.
483#[derive(Debug, Clone, PartialEq, Eq)]
484pub struct CacheInvalidateStepDef {
485    /// Name of the registered `CacheRepository` (optional; defaults to system default).
486    pub repository: Option<String>,
487    /// Expression that extracts the cache key to invalidate.
488    pub key: LanguageExpressionDef,
489}
490
491/// Cache Peek Stale EIP step definition.
492///
493/// Returns cached data even if TTL has expired (graceful degradation).
494#[derive(Debug, Clone, PartialEq, Eq)]
495pub struct CachePeekStaleStepDef {
496    /// Name of the registered `CacheRepository` (optional; defaults to system default).
497    pub repository: Option<String>,
498    /// Expression that extracts the cache key to peek.
499    pub key: LanguageExpressionDef,
500}
501
502/// Sampling EIP step definition.
503///
504/// Passes 1 of every N exchanges (counter-based, deterministic).
505#[derive(Debug, Clone, PartialEq, Eq)]
506pub struct SamplingStepDef {
507    /// Sampling period: 1 of every `period` exchanges passes.
508    pub period: usize,
509}
510
511/// Sort EIP step definition.
512///
513/// Orders a body collection by extracting a sort key from each element
514/// via a language expression.
515#[derive(Debug, Clone, PartialEq, Eq)]
516pub struct SortStepDef {
517    /// Expression that produces the sort key for each element.
518    pub expression: LanguageExpressionDef,
519    /// Reverse (descending) sort when true. Default false (ascending).
520    pub reverse: bool,
521}
522
523/// Resequence EIP step definition (Phase 3).
524///
525/// Supports batch and stream modes.
526#[derive(Debug, Clone, PartialEq, Eq)]
527pub struct ResequenceStepDef {
528    pub mode: ResequenceModeDef,
529}
530
531/// Resequence mode selection โ€” batch or stream.
532#[derive(Debug, Clone, PartialEq, Eq)]
533pub enum ResequenceModeDef {
534    Batch {
535        correlation: String,
536        sort: String,
537        completion: camel_api::resequencer::BatchCompletion,
538    },
539    Stream {
540        sequence: String,
541        capacity: usize,
542        gap_timeout: u64,
543        on_gap: camel_api::resequencer::GapPolicy,
544        on_capacity_exceeded: camel_api::resequencer::CapacityPolicy,
545        dedup: bool,
546    },
547}
548
549#[derive(Debug, Clone, PartialEq, Eq)]
550pub struct EnrichStepDef {
551    pub uri: String,
552    pub strategy: Option<String>,
553    pub timeout_ms: Option<u64>,
554}
555
556#[derive(Debug, Clone, PartialEq)]
557pub struct DoTryCatchClauseDef {
558    pub exception: Option<Vec<String>>,
559    pub when: Option<LanguageExpressionDef>,
560    pub on_when: Option<LanguageExpressionDef>,
561    pub disposition: camel_api::error_handler::ExceptionDisposition,
562    pub steps: Vec<DeclarativeStep>,
563}
564
565#[derive(Debug, Clone, PartialEq)]
566pub struct DoTryFinallyDef {
567    pub on_when: Option<LanguageExpressionDef>,
568    pub steps: Vec<DeclarativeStep>,
569}
570
571#[derive(Debug, Clone, PartialEq)]
572pub enum DeclarativeStep {
573    To(ToStepDef),
574    SetHeader(SetHeaderStepDef),
575    SetHeaderIfAbsent(SetHeaderStepDef),
576    SetProperty(SetPropertyStepDef),
577    SetBody(SetBodyStepDef),
578    ConvertBodyTo(BodyTypeDef),
579    DynamicRouter(DynamicRouterStepDef),
580    Filter(FilterStepDef),
581    Function(FunctionStepDef),
582    LoadBalance(LoadBalanceStepDef),
583    Log(LogStepDef),
584    Choice(ChoiceStepDef),
585    Split(SplitStepDef),
586    Aggregate(AggregateStepDef),
587    WireTap(WireTapStepDef),
588    Multicast(MulticastStepDef),
589    RoutingSlip(RoutingSlipStepDef),
590    RecipientList(RecipientListStepDef),
591    Stop,
592    Throttle(ThrottleStepDef),
593    Script(ScriptStepDef),
594    StreamCache(StreamCacheStepDef),
595    Marshal(DataFormatDef),
596    Unmarshal(DataFormatDef),
597    Validate(ValidateStepDef),
598    Bean(BeanStepDef),
599    Delay(DelayStepDef),
600    Loop(LoopStepDef),
601    Enrich(EnrichStepDef),
602    PollEnrich(EnrichStepDef),
603    IdempotentConsumer(IdempotentConsumerStepDef),
604    Cache(CacheStepDef),
605    CacheInvalidate(CacheInvalidateStepDef),
606    CachePeekStale(CachePeekStaleStepDef),
607    ClaimCheck(ClaimCheckStepDef),
608    Sampling(SamplingStepDef),
609    Sort(SortStepDef),
610    Resequence(ResequenceStepDef),
611    DoTry {
612        steps: Vec<DeclarativeStep>,
613        catch: Vec<DoTryCatchClauseDef>,
614        finally: Option<DoTryFinallyDef>,
615    },
616}
617
618impl DeclarativeStep {
619    pub fn kind(&self) -> crate::contract::DeclarativeStepKind {
620        match self {
621            DeclarativeStep::To(_) => crate::contract::DeclarativeStepKind::To,
622            DeclarativeStep::Log(_) => crate::contract::DeclarativeStepKind::Log,
623            DeclarativeStep::SetHeader(_) => crate::contract::DeclarativeStepKind::SetHeader,
624            DeclarativeStep::SetHeaderIfAbsent(_) => {
625                crate::contract::DeclarativeStepKind::SetHeaderIfAbsent
626            }
627            DeclarativeStep::SetProperty(_) => crate::contract::DeclarativeStepKind::SetProperty,
628            DeclarativeStep::SetBody(_) => crate::contract::DeclarativeStepKind::SetBody,
629            DeclarativeStep::ConvertBodyTo(_) => {
630                crate::contract::DeclarativeStepKind::ConvertBodyTo
631            }
632            DeclarativeStep::DynamicRouter(_) => {
633                crate::contract::DeclarativeStepKind::DynamicRouter
634            }
635            DeclarativeStep::Filter(_) => crate::contract::DeclarativeStepKind::Filter,
636            DeclarativeStep::Function(_) => crate::contract::DeclarativeStepKind::Function,
637            DeclarativeStep::LoadBalance(_) => crate::contract::DeclarativeStepKind::LoadBalance,
638            DeclarativeStep::Choice(_) => crate::contract::DeclarativeStepKind::Choice,
639            DeclarativeStep::Split(_) => crate::contract::DeclarativeStepKind::Split,
640            DeclarativeStep::Aggregate(_) => crate::contract::DeclarativeStepKind::Aggregate,
641            DeclarativeStep::WireTap(_) => crate::contract::DeclarativeStepKind::WireTap,
642            DeclarativeStep::Multicast(_) => crate::contract::DeclarativeStepKind::Multicast,
643            DeclarativeStep::RoutingSlip(_) => crate::contract::DeclarativeStepKind::RoutingSlip,
644            DeclarativeStep::RecipientList(_) => {
645                crate::contract::DeclarativeStepKind::RecipientList
646            }
647            DeclarativeStep::Stop => crate::contract::DeclarativeStepKind::Stop,
648            DeclarativeStep::Throttle(_) => crate::contract::DeclarativeStepKind::Throttle,
649            DeclarativeStep::Script(_) => crate::contract::DeclarativeStepKind::Script,
650            DeclarativeStep::StreamCache(_) => crate::contract::DeclarativeStepKind::StreamCache,
651            DeclarativeStep::Marshal(_) => crate::contract::DeclarativeStepKind::Marshal,
652            DeclarativeStep::Unmarshal(_) => crate::contract::DeclarativeStepKind::Unmarshal,
653            DeclarativeStep::Validate(_) => crate::contract::DeclarativeStepKind::Validate,
654            DeclarativeStep::Bean(_) => crate::contract::DeclarativeStepKind::Bean,
655            DeclarativeStep::Delay(_) => crate::contract::DeclarativeStepKind::Delay,
656            DeclarativeStep::Loop(_) => crate::contract::DeclarativeStepKind::Loop,
657            DeclarativeStep::Enrich(_) => crate::contract::DeclarativeStepKind::Enrich,
658            DeclarativeStep::PollEnrich(_) => crate::contract::DeclarativeStepKind::PollEnrich,
659            DeclarativeStep::IdempotentConsumer(_) => {
660                crate::contract::DeclarativeStepKind::IdempotentConsumer
661            }
662            DeclarativeStep::Cache(_) => crate::contract::DeclarativeStepKind::Cache,
663            DeclarativeStep::CacheInvalidate(_) => {
664                crate::contract::DeclarativeStepKind::CacheInvalidate
665            }
666            DeclarativeStep::CachePeekStale(_) => {
667                crate::contract::DeclarativeStepKind::CachePeekStale
668            }
669            DeclarativeStep::ClaimCheck(_) => crate::contract::DeclarativeStepKind::ClaimCheck,
670            DeclarativeStep::Sampling(_) => crate::contract::DeclarativeStepKind::Sampling,
671            DeclarativeStep::Sort(_) => crate::contract::DeclarativeStepKind::Sort,
672            DeclarativeStep::Resequence(_) => crate::contract::DeclarativeStepKind::Resequence,
673            DeclarativeStep::DoTry { .. } => crate::contract::DeclarativeStepKind::DoTry,
674        }
675    }
676}
677
678#[cfg(test)]
679mod tests {
680    use super::*;
681
682    #[test]
683    fn to_step_def_new() {
684        let def = ToStepDef::new("direct:a");
685        assert_eq!(def.uri, "direct:a");
686    }
687
688    #[test]
689    fn log_step_def_info() {
690        let def = LogStepDef::info("hello");
691        assert_eq!(def.level, LogLevelDef::Info);
692        match def.message {
693            ValueSourceDef::Literal(v) => assert_eq!(v, serde_json::Value::String("hello".into())),
694            _ => panic!("expected literal"),
695        }
696    }
697
698    #[test]
699    fn set_header_literal() {
700        let def = SetHeaderStepDef::literal("key", "value");
701        assert_eq!(def.key, "key");
702        match def.value {
703            ValueSourceDef::Literal(v) => assert_eq!(v, serde_json::Value::String("value".into())),
704            _ => panic!("expected literal"),
705        }
706    }
707
708    #[test]
709    fn bean_step_def_new() {
710        let def = BeanStepDef::new("myBean", "process");
711        assert_eq!(def.name, "myBean");
712        assert_eq!(def.method, "process");
713    }
714
715    #[test]
716    fn throttle_strategy_default() {
717        assert_eq!(ThrottleStrategyDef::default(), ThrottleStrategyDef::Delay);
718    }
719
720    #[test]
721    fn load_balance_strategy_default() {
722        assert_eq!(
723            LoadBalanceStrategyDef::default(),
724            LoadBalanceStrategyDef::RoundRobin
725        );
726    }
727
728    #[test]
729    fn concurrency_variants_equality() {
730        assert_eq!(
731            DeclarativeConcurrency::Sequential,
732            DeclarativeConcurrency::Sequential
733        );
734        assert_ne!(
735            DeclarativeConcurrency::Sequential,
736            DeclarativeConcurrency::Concurrent { max: None }
737        );
738    }
739
740    #[test]
741    fn body_type_variants() {
742        assert_eq!(BodyTypeDef::Text, BodyTypeDef::Text);
743        assert_ne!(BodyTypeDef::Text, BodyTypeDef::Json);
744    }
745
746    #[test]
747    fn data_format_def() {
748        let def = DataFormatDef {
749            format: "protobuf".into(),
750            schema: None,
751            config: None,
752        };
753        assert_eq!(def.format, "protobuf");
754        assert!(def.schema.is_none());
755    }
756
757    #[test]
758    fn stream_cache_step_def() {
759        let def = StreamCacheStepDef {
760            threshold: Some(1024),
761        };
762        assert_eq!(def.threshold, Some(1024));
763    }
764
765    #[test]
766    fn delay_step_def() {
767        let def = DelayStepDef {
768            delay_ms: 500,
769            dynamic_header: Some("X-Delay".into()),
770        };
771        assert_eq!(def.delay_ms, 500);
772        assert_eq!(def.dynamic_header.as_deref(), Some("X-Delay"));
773    }
774
775    #[test]
776    fn circuit_breaker_def() {
777        let cb = DeclarativeCircuitBreaker {
778            failure_threshold: 3,
779            open_duration_ms: 5000,
780        };
781        assert_eq!(cb.failure_threshold, 3);
782        assert_eq!(cb.open_duration_ms, 5000);
783    }
784}