|
1 |
| -use std::ops::Deref; |
| 1 | +use std::{fmt, ops::Deref}; |
2 | 2 |
|
3 | 3 | use vector_config::configurable_component;
|
4 | 4 | pub use vector_core::config::ComponentKey;
|
5 | 5 |
|
| 6 | +use super::schema; |
| 7 | + |
6 | 8 | /// A list of upstream [source][sources] or [transform][transforms] IDs.
|
7 | 9 | ///
|
8 | 10 | /// Wildcards (`*`) are supported.
|
@@ -94,3 +96,88 @@ impl<T> From<Vec<T>> for Inputs<T> {
|
94 | 96 | Self(inputs)
|
95 | 97 | }
|
96 | 98 | }
|
| 99 | + |
| 100 | +/// Component output identifier. |
| 101 | +#[configurable_component] |
| 102 | +#[derive(Clone, Debug, Eq, Hash, PartialEq)] |
| 103 | +pub struct OutputId { |
| 104 | + /// The component to which the output belongs. |
| 105 | + pub component: ComponentKey, |
| 106 | + |
| 107 | + /// The output port name, if not the default. |
| 108 | + pub port: Option<String>, |
| 109 | +} |
| 110 | + |
| 111 | +impl OutputId { |
| 112 | + /// Some situations, for example when validating a config file requires running the |
| 113 | + /// transforms::output function to retrieve the outputs, but we don't have an |
| 114 | + /// `OutputId` from a source. This gives us an `OutputId` that we can use. |
| 115 | + /// |
| 116 | + /// TODO: This is not a pleasant solution, but would require some significant refactoring |
| 117 | + /// to the topology code to avoid. |
| 118 | + pub fn dummy() -> Self { |
| 119 | + Self { |
| 120 | + component: "dummy".into(), |
| 121 | + port: None, |
| 122 | + } |
| 123 | + } |
| 124 | + |
| 125 | + /// Given a list of [`schema::Definition`]s, returns a [`Vec`] of tuples of |
| 126 | + /// this `OutputId` with each `Definition`. |
| 127 | + pub fn with_definitions( |
| 128 | + &self, |
| 129 | + definitions: impl IntoIterator<Item = schema::Definition>, |
| 130 | + ) -> Vec<(OutputId, schema::Definition)> { |
| 131 | + definitions |
| 132 | + .into_iter() |
| 133 | + .map(|definition| (self.clone(), definition)) |
| 134 | + .collect() |
| 135 | + } |
| 136 | +} |
| 137 | + |
| 138 | +impl fmt::Display for OutputId { |
| 139 | + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 140 | + match &self.port { |
| 141 | + None => self.component.fmt(f), |
| 142 | + Some(port) => write!(f, "{}.{}", self.component, port), |
| 143 | + } |
| 144 | + } |
| 145 | +} |
| 146 | + |
| 147 | +impl From<ComponentKey> for OutputId { |
| 148 | + fn from(key: ComponentKey) -> Self { |
| 149 | + Self { |
| 150 | + component: key, |
| 151 | + port: None, |
| 152 | + } |
| 153 | + } |
| 154 | +} |
| 155 | + |
| 156 | +impl From<&ComponentKey> for OutputId { |
| 157 | + fn from(key: &ComponentKey) -> Self { |
| 158 | + Self::from(key.clone()) |
| 159 | + } |
| 160 | +} |
| 161 | + |
| 162 | +impl From<(&ComponentKey, String)> for OutputId { |
| 163 | + fn from((key, name): (&ComponentKey, String)) -> Self { |
| 164 | + Self { |
| 165 | + component: key.clone(), |
| 166 | + port: Some(name), |
| 167 | + } |
| 168 | + } |
| 169 | +} |
| 170 | + |
| 171 | +// This panicking implementation is convenient for testing, but should never be enabled for use |
| 172 | +// outside of tests. |
| 173 | +#[cfg(test)] |
| 174 | +impl From<&str> for OutputId { |
| 175 | + fn from(s: &str) -> Self { |
| 176 | + assert!( |
| 177 | + !s.contains('.'), |
| 178 | + "Cannot convert dotted paths to strings without more context" |
| 179 | + ); |
| 180 | + let component = ComponentKey::from(s); |
| 181 | + component.into() |
| 182 | + } |
| 183 | +} |
0 commit comments