vector_config/component/
description.rs

1use std::{cell::RefCell, marker::PhantomData};
2
3use snafu::Snafu;
4use toml::Value;
5use vector_config_common::{attributes::CustomAttribute, constants};
6
7use super::{ComponentMarker, GenerateConfig};
8use crate::schema::{SchemaGenerator, SchemaObject};
9use crate::{schema, Configurable, ConfigurableRef, GenerateError, Metadata};
10
11#[derive(Debug, Snafu, Clone, PartialEq, Eq)]
12pub enum ExampleError {
13    #[snafu(display("component '{}' does not exist", component_name))]
14    DoesNotExist { component_name: String },
15}
16
17/// Description of a component.
18pub struct ComponentDescription<T: ComponentMarker + Sized> {
19    component_name: &'static str,
20    description: &'static str,
21    label: &'static str,
22    logical_name: &'static str,
23    example_value: fn() -> Value,
24    config: ConfigurableRef,
25    _component_type: PhantomData<T>,
26}
27
28impl<T> ComponentDescription<T>
29where
30    T: ComponentMarker + Sized + 'static,
31    inventory::iter<ComponentDescription<T>>:
32        std::iter::IntoIterator<Item = &'static ComponentDescription<T>>,
33{
34    /// Creates a new `ComponentDescription`.
35    ///
36    /// This creates a component description for a component identified both by the given component
37    /// type `T` and the component name. As such, if `T` is `SourceComponent`, and the name is
38    /// `stdin`, you would say that the component is a "source called `stdin`".
39    ///
40    /// The type parameter `C` must be the component's configuration type that implements
41    /// `Configurable` and `GenerateConfig`.
42    pub const fn new<C: GenerateConfig + Configurable + 'static>(
43        component_name: &'static str,
44        label: &'static str,
45        logical_name: &'static str,
46        description: &'static str,
47    ) -> Self {
48        ComponentDescription {
49            component_name,
50            description,
51            label,
52            logical_name,
53            example_value: C::generate_config,
54            config: ConfigurableRef::new::<C>(),
55            _component_type: PhantomData,
56        }
57    }
58
59    /// Generates an example configuration for the component with the given component name.
60    ///
61    /// ## Errors
62    ///
63    /// If no component, identified by `T` and the given name, is registered, or if there is an
64    /// error generating the example configuration, an error variant will be returned.
65    pub fn example(component_name: &str) -> Result<Value, ExampleError> {
66        inventory::iter::<ComponentDescription<T>>
67            .into_iter()
68            .find(|t| t.component_name == component_name)
69            .ok_or_else(|| ExampleError::DoesNotExist {
70                component_name: component_name.to_owned(),
71            })
72            .map(|t| (t.example_value)())
73    }
74
75    /// Gets a sorted list of all registered components of the given component type.
76    pub fn types() -> Vec<&'static str> {
77        let mut types = Vec::new();
78        for definition in inventory::iter::<ComponentDescription<T>> {
79            types.push(definition.component_name);
80        }
81        types.sort_unstable();
82        types
83    }
84
85    /// Generate a schema object covering all the descriptions of this type.
86    pub fn generate_schemas(gen: &RefCell<SchemaGenerator>) -> Result<SchemaObject, GenerateError> {
87        let mut descriptions: Vec<_> = inventory::iter::<Self>.into_iter().collect();
88        descriptions.sort_unstable_by_key(|desc| desc.component_name);
89        let subschemas: Vec<SchemaObject> = descriptions
90            .into_iter()
91            .map(|description| description.generate_schema(gen))
92            .collect::<Result<_, _>>()?;
93        Ok(schema::generate_one_of_schema(&subschemas))
94    }
95
96    /// Generate a schema object for this description.
97    fn generate_schema(
98        &self,
99        gen: &RefCell<SchemaGenerator>,
100    ) -> Result<SchemaObject, GenerateError> {
101        let mut tag_subschema =
102            schema::generate_const_string_schema(self.component_name.to_string());
103        let variant_tag_metadata = Metadata::with_description(self.description);
104        schema::apply_base_metadata(&mut tag_subschema, variant_tag_metadata);
105
106        let tag_schema =
107            schema::generate_internal_tagged_variant_schema("type".to_string(), tag_subschema);
108        let flattened_subschemas = vec![tag_schema];
109
110        let mut field_metadata = Metadata::default();
111        field_metadata.set_transparent();
112        let mut subschema =
113            schema::get_or_generate_schema(&self.config, gen, Some(field_metadata))?;
114
115        schema::convert_to_flattened_schema(&mut subschema, flattened_subschemas);
116
117        let mut variant_metadata = Metadata::default();
118        variant_metadata.set_description(self.description);
119        variant_metadata.add_custom_attribute(CustomAttribute::kv(
120            constants::DOCS_META_HUMAN_NAME,
121            self.label,
122        ));
123        variant_metadata
124            .add_custom_attribute(CustomAttribute::kv("logical_name", self.logical_name));
125        schema::apply_base_metadata(&mut subschema, variant_metadata);
126
127        Ok(subschema)
128    }
129}