greentic_pack/
messaging.rs1use std::collections::BTreeSet;
2
3use anyhow::{Result, bail};
4use schemars::JsonSchema;
5use serde::{Deserialize, Serialize};
6
7#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, Default)]
8pub struct MessagingSection {
9 #[serde(default, skip_serializing_if = "Option::is_none")]
10 pub adapters: Option<Vec<MessagingAdapter>>,
11}
12
13impl MessagingSection {
14 pub fn validate(&self) -> Result<()> {
15 let mut seen = BTreeSet::new();
16 if let Some(adapters) = &self.adapters {
17 for adapter in adapters {
18 adapter.validate()?;
19 if !seen.insert(adapter.name.clone()) {
20 bail!("duplicate messaging adapter name: {}", adapter.name);
21 }
22 }
23 }
24 Ok(())
25 }
26}
27
28#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
29pub struct MessagingAdapter {
30 pub name: String,
31 pub kind: MessagingAdapterKind,
32 pub component: String,
33 #[serde(default, skip_serializing_if = "Option::is_none")]
34 pub default_flow: Option<String>,
35 #[serde(default, skip_serializing_if = "Option::is_none")]
36 pub custom_flow: Option<String>,
37 #[serde(default, skip_serializing_if = "Option::is_none")]
38 pub capabilities: Option<MessagingAdapterCapabilities>,
39}
40
41impl MessagingAdapter {
42 fn validate(&self) -> Result<()> {
43 if self.name.trim().is_empty() {
44 bail!("messaging.adapters[].name is required");
45 }
46 if self.component.trim().is_empty() {
47 bail!(
48 "messaging.adapters[{}].component must not be empty",
49 self.name
50 );
51 }
52 if let Some(cap) = &self.capabilities {
53 cap.validate(&self.name)?;
54 }
55 Ok(())
56 }
57}
58
59#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
60#[serde(rename_all = "kebab-case")]
61pub enum MessagingAdapterKind {
62 Ingress,
63 Egress,
64 IngressEgress,
65}
66
67#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, Default)]
68#[serde(deny_unknown_fields)]
69pub struct MessagingAdapterCapabilities {
70 #[serde(default, skip_serializing_if = "Vec::is_empty")]
71 pub direction: Vec<String>,
72 #[serde(default, skip_serializing_if = "Vec::is_empty")]
73 pub features: Vec<String>,
74}
75
76impl MessagingAdapterCapabilities {
77 fn validate(&self, name: &str) -> Result<()> {
78 for entry in &self.direction {
79 if entry.trim().is_empty() {
80 bail!(
81 "messaging.adapters[{name}].capabilities.direction must not contain empty values"
82 );
83 }
84 }
85 for entry in &self.features {
86 if entry.trim().is_empty() {
87 bail!(
88 "messaging.adapters[{name}].capabilities.features must not contain empty values"
89 );
90 }
91 }
92 Ok(())
93 }
94}