vector_core/config/
output_id.rs

1use std::fmt;
2
3use vector_common::config::ComponentKey;
4
5use super::configurable_component;
6use crate::schema;
7
8/// Component output identifier.
9#[configurable_component]
10#[derive(Clone, Debug, Eq, Hash, PartialEq)]
11pub struct OutputId {
12    /// The component to which the output belongs.
13    pub component: ComponentKey,
14
15    /// The output port name, if not the default.
16    pub port: Option<String>,
17}
18
19impl OutputId {
20    /// Some situations, for example when validating a config file requires running the
21    /// `transforms::output` function to retrieve the outputs, but we don't have an
22    /// `OutputId` from a source. This gives us an `OutputId` that we can use.
23    ///
24    /// TODO: This is not a pleasant solution, but would require some significant refactoring
25    /// to the topology code to avoid.
26    pub fn dummy() -> Self {
27        Self {
28            component: "dummy".into(),
29            port: None,
30        }
31    }
32
33    /// Given a list of [`schema::Definition`]s, returns a [`Vec`] of tuples of
34    /// this `OutputId` with each `Definition`.
35    pub fn with_definitions(
36        &self,
37        definitions: impl IntoIterator<Item = schema::Definition>,
38    ) -> Vec<(OutputId, schema::Definition)> {
39        definitions
40            .into_iter()
41            .map(|definition| (self.clone(), definition))
42            .collect()
43    }
44}
45
46impl fmt::Display for OutputId {
47    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48        match &self.port {
49            None => self.component.fmt(f),
50            Some(port) => write!(f, "{}.{port}", self.component),
51        }
52    }
53}
54
55impl From<ComponentKey> for OutputId {
56    fn from(key: ComponentKey) -> Self {
57        Self {
58            component: key,
59            port: None,
60        }
61    }
62}
63
64impl From<&ComponentKey> for OutputId {
65    fn from(key: &ComponentKey) -> Self {
66        Self::from(key.clone())
67    }
68}
69
70impl From<(&ComponentKey, String)> for OutputId {
71    fn from((key, name): (&ComponentKey, String)) -> Self {
72        Self {
73            component: key.clone(),
74            port: Some(name),
75        }
76    }
77}
78
79impl From<(String, Option<String>)> for OutputId {
80    fn from((component, port): (String, Option<String>)) -> Self {
81        Self {
82            component: component.into(),
83            port,
84        }
85    }
86}
87
88// This panicking implementation is convenient for testing, but should never be enabled for use
89// outside of tests.
90#[cfg(any(test, feature = "test"))]
91impl From<&str> for OutputId {
92    fn from(s: &str) -> Self {
93        assert!(
94            !s.contains('.'),
95            "Cannot convert dotted paths to strings without more context"
96        );
97        let component = ComponentKey::from(s);
98        component.into()
99    }
100}