1use std::fmt;
4
5use crate::{support::SubsystemIdentifier, topology::ComponentId};
6
7pub mod decoders;
8pub mod destinations;
9pub mod encoders;
10pub mod forwarders;
11pub mod relays;
12pub mod sources;
13pub mod transforms;
14
15mod build_context;
16pub use self::build_context::BuildContext;
17
18#[cfg(any(test, feature = "test-util"))]
19pub mod test_util;
20
21#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
23pub enum ComponentType {
24 Source,
26
27 Relay,
29
30 Decoder,
32
33 Transform,
35
36 Encoder,
38
39 Forwarder,
41
42 Destination,
44}
45
46impl ComponentType {
47 pub fn as_str(&self) -> &'static str {
49 match self {
50 Self::Source => "source",
51 Self::Relay => "relay",
52 Self::Decoder => "decoder",
53 Self::Transform => "transform",
54 Self::Encoder => "encoder",
55 Self::Forwarder => "forwarder",
56 Self::Destination => "destination",
57 }
58 }
59
60 pub fn as_category_str(&self) -> &'static str {
65 match self {
66 Self::Source => "sources",
67 Self::Relay => "relays",
68 Self::Decoder => "decoders",
69 Self::Transform => "transforms",
70 Self::Encoder => "encoders",
71 Self::Forwarder => "forwarders",
72 Self::Destination => "destinations",
73 }
74 }
75}
76
77#[derive(Clone, Debug, Eq, Hash, PartialEq)]
81pub struct ComponentContext {
82 topology_root: SubsystemIdentifier,
83 component_id: ComponentId,
84 component_type: ComponentType,
85}
86
87impl ComponentContext {
88 pub fn new(topology_root: &SubsystemIdentifier, component_id: ComponentId, component_type: ComponentType) -> Self {
90 Self {
91 topology_root: topology_root.clone(),
92 component_id,
93 component_type,
94 }
95 }
96
97 pub fn source(topology_root: &SubsystemIdentifier, component_id: ComponentId) -> Self {
99 Self::new(topology_root, component_id, ComponentType::Source)
100 }
101
102 pub fn relay(topology_root: &SubsystemIdentifier, component_id: ComponentId) -> Self {
104 Self::new(topology_root, component_id, ComponentType::Relay)
105 }
106
107 pub fn decoder(topology_root: &SubsystemIdentifier, component_id: ComponentId) -> Self {
109 Self::new(topology_root, component_id, ComponentType::Decoder)
110 }
111
112 pub fn transform(topology_root: &SubsystemIdentifier, component_id: ComponentId) -> Self {
114 Self::new(topology_root, component_id, ComponentType::Transform)
115 }
116
117 pub fn encoder(topology_root: &SubsystemIdentifier, component_id: ComponentId) -> Self {
119 Self::new(topology_root, component_id, ComponentType::Encoder)
120 }
121
122 pub fn forwarder(topology_root: &SubsystemIdentifier, component_id: ComponentId) -> Self {
124 Self::new(topology_root, component_id, ComponentType::Forwarder)
125 }
126
127 pub fn destination(topology_root: &SubsystemIdentifier, component_id: ComponentId) -> Self {
129 Self::new(topology_root, component_id, ComponentType::Destination)
130 }
131
132 #[cfg(any(test, feature = "test-util"))]
134 pub fn test_source<S: AsRef<str>>(component_id: S) -> Self {
135 Self::source(
136 &SubsystemIdentifier::from_segments(["topology", "test"]),
137 ComponentId::try_from(component_id.as_ref()).expect("invalid component ID"),
138 )
139 }
140
141 #[cfg(any(test, feature = "test-util"))]
143 pub fn test_relay<S: AsRef<str>>(component_id: S) -> Self {
144 Self::relay(
145 &SubsystemIdentifier::from_segments(["topology", "test"]),
146 ComponentId::try_from(component_id.as_ref()).expect("invalid component ID"),
147 )
148 }
149
150 #[cfg(any(test, feature = "test-util"))]
152 pub fn test_decoder<S: AsRef<str>>(component_id: S) -> Self {
153 Self::decoder(
154 &SubsystemIdentifier::from_segments(["topology", "test"]),
155 ComponentId::try_from(component_id.as_ref()).expect("invalid component ID"),
156 )
157 }
158
159 #[cfg(any(test, feature = "test-util"))]
161 pub fn test_transform<S: AsRef<str>>(component_id: S) -> Self {
162 Self::transform(
163 &SubsystemIdentifier::from_segments(["topology", "test"]),
164 ComponentId::try_from(component_id.as_ref()).expect("invalid component ID"),
165 )
166 }
167
168 #[cfg(any(test, feature = "test-util"))]
170 pub fn test_encoder<S: AsRef<str>>(component_id: S) -> Self {
171 Self::encoder(
172 &SubsystemIdentifier::from_segments(["topology", "test"]),
173 ComponentId::try_from(component_id.as_ref()).expect("invalid component ID"),
174 )
175 }
176
177 #[cfg(any(test, feature = "test-util"))]
179 pub fn test_forwarder<S: AsRef<str>>(component_id: S) -> Self {
180 Self::forwarder(
181 &SubsystemIdentifier::from_segments(["topology", "test"]),
182 ComponentId::try_from(component_id.as_ref()).expect("invalid component ID"),
183 )
184 }
185
186 #[cfg(any(test, feature = "test-util"))]
188 pub fn test_destination<S: AsRef<str>>(component_id: S) -> Self {
189 Self::destination(
190 &SubsystemIdentifier::from_segments(["topology", "test"]),
191 ComponentId::try_from(component_id.as_ref()).expect("invalid component ID"),
192 )
193 }
194
195 pub fn component_id(&self) -> &ComponentId {
200 &self.component_id
201 }
202
203 pub fn component_type(&self) -> ComponentType {
205 self.component_type
206 }
207
208 pub fn identity(&self) -> SubsystemIdentifier {
213 self.topology_root
214 .clone()
215 .child(self.component_type.as_category_str())
216 .child(&*self.component_id)
217 }
218}
219
220impl fmt::Display for ComponentContext {
221 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
222 write!(f, "{}", self.identity())
223 }
224}
225
226#[cfg(test)]
227mod tests {
228 use std::sync::Arc;
229
230 use saluki_common::sync::shutdown::ShutdownHandle;
231 use tokio::runtime::Handle;
232 use tokio::sync::mpsc;
233
234 use super::decoders::DecoderContext;
235 use super::destinations::DestinationContext;
236 use super::encoders::EncoderContext;
237 use super::forwarders::ForwarderContext;
238 use super::relays::RelayContext;
239 use super::sources::SourceContext;
240 use super::transforms::TransformContext;
241 use super::ComponentContext;
242 use crate::accounting::{ComponentRegistry, MemoryLimiter};
243 use crate::health::{Health, HealthRegistry};
244 use crate::runtime::state::DataspaceRegistry;
245 use crate::support::SubsystemIdentifier;
246 use crate::topology::interconnect::{Consumer, Dispatcher};
247 use crate::topology::{
248 EventsBuffer, EventsConsumer, EventsDispatcher, PayloadsBuffer, PayloadsConsumer, PayloadsDispatcher,
249 TopologyContext,
250 };
251
252 #[test]
253 fn identity_dotted_form() {
254 let context = ComponentContext::test_source("dsd_in");
255 assert_eq!(context.identity().to_string(), "topology.test.sources.dsd_in");
256 }
257
258 #[test]
259 fn context_display_equals_identity_to_string() {
260 let context = ComponentContext::test_transform("dsd_mapper");
261 assert_eq!(context.to_string(), context.identity().to_string());
262 }
263
264 fn topology_context() -> TopologyContext {
274 TopologyContext::new(
275 Arc::from("test"),
276 MemoryLimiter::noop(),
277 HealthRegistry::new(),
278 Handle::current(),
279 DataspaceRegistry::new(),
280 )
281 }
282
283 fn health_handle() -> Health {
284 HealthRegistry::new()
285 .register_component(&SubsystemIdentifier::from_dotted("test"))
286 .expect("component was not previously registered")
287 }
288
289 fn events_dispatcher(component_context: &ComponentContext) -> EventsDispatcher {
290 Dispatcher::new(component_context.clone())
291 }
292
293 fn payloads_dispatcher(component_context: &ComponentContext) -> PayloadsDispatcher {
294 Dispatcher::new(component_context.clone())
295 }
296
297 fn events_consumer(component_context: &ComponentContext) -> EventsConsumer {
298 let (_tx, rx) = mpsc::channel::<EventsBuffer>(1);
299 Consumer::new(component_context.clone(), rx)
300 }
301
302 fn payloads_consumer(component_context: &ComponentContext) -> PayloadsConsumer {
303 let (_tx, rx) = mpsc::channel::<PayloadsBuffer>(1);
304 Consumer::new(component_context.clone(), rx)
305 }
306
307 fn source_context() -> SourceContext {
308 let cc = ComponentContext::test_source("test");
309 SourceContext::new(
310 &topology_context(),
311 &cc,
312 ComponentRegistry::default(),
313 health_handle(),
314 events_dispatcher(&cc),
315 )
316 }
317
318 fn relay_context() -> RelayContext {
319 let cc = ComponentContext::test_relay("test");
320 RelayContext::new(
321 &topology_context(),
322 &cc,
323 ComponentRegistry::default(),
324 health_handle(),
325 payloads_dispatcher(&cc),
326 )
327 }
328
329 fn decoder_context() -> DecoderContext {
330 let cc = ComponentContext::test_decoder("test");
331 DecoderContext::new(
332 &topology_context(),
333 &cc,
334 ComponentRegistry::default(),
335 health_handle(),
336 events_dispatcher(&cc),
337 payloads_consumer(&cc),
338 )
339 }
340
341 fn transform_context() -> TransformContext {
342 let cc = ComponentContext::test_transform("test");
343 TransformContext::new(
344 &topology_context(),
345 &cc,
346 ComponentRegistry::default(),
347 health_handle(),
348 events_dispatcher(&cc),
349 events_consumer(&cc),
350 )
351 }
352
353 fn destination_context() -> DestinationContext {
354 let cc = ComponentContext::test_destination("test");
355 DestinationContext::new(
356 &topology_context(),
357 &cc,
358 ComponentRegistry::default(),
359 health_handle(),
360 events_consumer(&cc),
361 )
362 }
363
364 fn encoder_context() -> EncoderContext {
365 let cc = ComponentContext::test_encoder("test");
366 EncoderContext::new(
367 &topology_context(),
368 &cc,
369 ComponentRegistry::default(),
370 health_handle(),
371 payloads_dispatcher(&cc),
372 events_consumer(&cc),
373 )
374 }
375
376 fn forwarder_context() -> ForwarderContext {
377 let cc = ComponentContext::test_forwarder("test");
378 ForwarderContext::new(
379 &topology_context(),
380 &cc,
381 ComponentRegistry::default(),
382 health_handle(),
383 payloads_consumer(&cc),
384 )
385 }
386
387 #[tokio::test]
390 #[should_panic(expected = "health handle already taken")]
391 async fn source_context_panics_on_double_take_of_health_handle() {
392 let mut ctx = source_context();
393 let _first = ctx.take_health_handle();
394 let _second = ctx.take_health_handle();
395 }
396
397 #[tokio::test]
398 #[should_panic(expected = "health handle already taken")]
399 async fn relay_context_panics_on_double_take_of_health_handle() {
400 let mut ctx = relay_context();
401 let _first = ctx.take_health_handle();
402 let _second = ctx.take_health_handle();
403 }
404
405 #[tokio::test]
406 #[should_panic(expected = "health handle already taken")]
407 async fn decoder_context_panics_on_double_take_of_health_handle() {
408 let mut ctx = decoder_context();
409 let _first = ctx.take_health_handle();
410 let _second = ctx.take_health_handle();
411 }
412
413 #[tokio::test]
414 #[should_panic(expected = "health handle already taken")]
415 async fn transform_context_panics_on_double_take_of_health_handle() {
416 let mut ctx = transform_context();
417 let _first = ctx.take_health_handle();
418 let _second = ctx.take_health_handle();
419 }
420
421 #[tokio::test]
422 #[should_panic(expected = "health handle already taken")]
423 async fn destination_context_panics_on_double_take_of_health_handle() {
424 let mut ctx = destination_context();
425 let _first = ctx.take_health_handle();
426 let _second = ctx.take_health_handle();
427 }
428
429 #[tokio::test]
430 #[should_panic(expected = "health handle already taken")]
431 async fn encoder_context_panics_on_double_take_of_health_handle() {
432 let mut ctx = encoder_context();
433 let _first = ctx.take_health_handle();
434 let _second = ctx.take_health_handle();
435 }
436
437 #[tokio::test]
438 #[should_panic(expected = "health handle already taken")]
439 async fn forwarder_context_panics_on_double_take_of_health_handle() {
440 let mut ctx = forwarder_context();
441 let _first = ctx.take_health_handle();
442 let _second = ctx.take_health_handle();
443 }
444
445 #[tokio::test]
449 #[should_panic(expected = "shutdown handle already taken")]
450 async fn source_context_panics_on_double_take_of_shutdown_handle() {
451 let mut ctx = source_context();
452 ctx.set_shutdown_handle(ShutdownHandle::noop());
453 let _first = ctx.take_shutdown_handle();
454 let _second = ctx.take_shutdown_handle();
455 }
456
457 #[tokio::test]
458 #[should_panic(expected = "shutdown handle already taken")]
459 async fn relay_context_panics_on_double_take_of_shutdown_handle() {
460 let mut ctx = relay_context();
461 ctx.set_shutdown_handle(ShutdownHandle::noop());
462 let _first = ctx.take_shutdown_handle();
463 let _second = ctx.take_shutdown_handle();
464 }
465}