Skip to main content

distributed/microsvc/
descriptor.rs

1use serde::{Deserialize, Serialize};
2
3/// Deployment/service endpoint metadata kept outside the logical application
4/// contract. It is consumed by service tooling and never serialized into an
5/// [`crate::application::ApplicationManifest`].
6#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
7pub struct ServiceDescriptor {
8    pub name: String,
9    pub commands: Vec<MessageEndpointDescriptor>,
10    pub events: Vec<MessageEndpointDescriptor>,
11    pub transports: Vec<TransportDescriptor>,
12    #[serde(default, skip_serializing_if = "Option::is_none")]
13    pub observability: Option<ServiceObservabilityDescriptor>,
14}
15
16impl ServiceDescriptor {
17    pub fn new(name: impl Into<String>) -> Self {
18        Self {
19            name: name.into(),
20            commands: Vec::new(),
21            events: Vec::new(),
22            transports: Vec::new(),
23            observability: None,
24        }
25    }
26
27    pub fn command(mut self, name: impl Into<String>) -> Self {
28        self.commands.push(MessageEndpointDescriptor::new(name));
29        self
30    }
31
32    pub fn event(mut self, name: impl Into<String>) -> Self {
33        self.events.push(MessageEndpointDescriptor::new(name));
34        self
35    }
36
37    pub fn transport(mut self, kind: impl Into<String>) -> Self {
38        self.transports.push(TransportDescriptor::new(kind));
39        self
40    }
41
42    pub fn observability(mut self, observability: ServiceObservabilityDescriptor) -> Self {
43        self.observability = Some(observability);
44        self
45    }
46
47    pub fn metrics(mut self, metrics: MetricsEndpointDescriptor) -> Self {
48        let mut observability = self.observability.unwrap_or_default();
49        observability.metrics = Some(metrics);
50        self.observability = Some(observability);
51        self
52    }
53
54    pub fn tracing(mut self, tracing: TracingDescriptor) -> Self {
55        let mut observability = self.observability.unwrap_or_default();
56        observability.tracing = Some(tracing);
57        self.observability = Some(observability);
58        self
59    }
60}
61
62#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
63pub struct ServiceObservabilityDescriptor {
64    #[serde(default, skip_serializing_if = "Option::is_none")]
65    pub metrics: Option<MetricsEndpointDescriptor>,
66    #[serde(default, skip_serializing_if = "Option::is_none")]
67    pub tracing: Option<TracingDescriptor>,
68}
69
70impl ServiceObservabilityDescriptor {
71    pub fn new() -> Self {
72        Self::default()
73    }
74
75    pub fn metrics(mut self, metrics: MetricsEndpointDescriptor) -> Self {
76        self.metrics = Some(metrics);
77        self
78    }
79
80    pub fn tracing(mut self, tracing: TracingDescriptor) -> Self {
81        self.tracing = Some(tracing);
82        self
83    }
84}
85
86#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
87pub struct MetricsEndpointDescriptor {
88    pub path: String,
89    pub port_name: String,
90    #[serde(default, skip_serializing_if = "Option::is_none")]
91    pub interval: Option<String>,
92}
93
94impl MetricsEndpointDescriptor {
95    pub fn new(path: impl Into<String>, port_name: impl Into<String>) -> Self {
96        Self {
97            path: path.into(),
98            port_name: port_name.into(),
99            interval: None,
100        }
101    }
102
103    pub fn prometheus_default() -> Self {
104        Self::new("/metrics", "http").interval("30s")
105    }
106
107    pub fn interval(mut self, interval: impl Into<String>) -> Self {
108        self.interval = Some(interval.into());
109        self
110    }
111}
112
113#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
114pub struct TracingDescriptor {
115    pub propagation: TracePropagationMode,
116    pub export: TraceExportMode,
117}
118
119impl TracingDescriptor {
120    pub fn otlp() -> Self {
121        Self {
122            propagation: TracePropagationMode::W3cTraceContext,
123            export: TraceExportMode::Otlp,
124        }
125    }
126
127    pub fn disabled() -> Self {
128        Self {
129            propagation: TracePropagationMode::Disabled,
130            export: TraceExportMode::Disabled,
131        }
132    }
133}
134
135#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
136#[serde(rename_all = "snake_case")]
137pub enum TracePropagationMode {
138    #[default]
139    W3cTraceContext,
140    Disabled,
141}
142
143#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
144#[serde(rename_all = "snake_case")]
145pub enum TraceExportMode {
146    #[default]
147    Otlp,
148    Disabled,
149}
150
151#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
152pub struct MessageEndpointDescriptor {
153    pub name: String,
154}
155
156impl MessageEndpointDescriptor {
157    pub fn new(name: impl Into<String>) -> Self {
158        Self { name: name.into() }
159    }
160}
161
162#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
163pub struct TransportDescriptor {
164    pub kind: String,
165}
166
167impl TransportDescriptor {
168    pub fn new(kind: impl Into<String>) -> Self {
169        Self { kind: kind.into() }
170    }
171
172    pub fn http() -> Self {
173        Self::new("http")
174    }
175}