vector_config/
configurable.rs

1#![deny(missing_docs)]
2
3use std::cell::RefCell;
4
5use serde_json::Value;
6
7use crate::{
8    schema::{SchemaGenerator, SchemaObject},
9    GenerateError, Metadata,
10};
11
12/// A type that can be represented in a Vector configuration.
13///
14/// In Vector, we want to be able to generate a schema for our configuration such that we can have a Rust-agnostic
15/// definition of exactly what is configurable, what values are allowed, what bounds exist, and so on and so forth.
16///
17/// `Configurable` provides the machinery to allow describing and encoding the shape of a type, recursively, so that by
18/// instrumenting all transitive types of the configuration, the schema can be discovered by generating the schema from
19/// some root type.
20pub trait Configurable {
21    /// Gets the referenceable name of this value, if any.
22    ///
23    /// When specified, this implies the value is both complex and standardized, and should be
24    /// reused within any generated schema it is present in.
25    fn referenceable_name() -> Option<&'static str>
26    where
27        Self: Sized,
28    {
29        None
30    }
31
32    /// Whether or not this value is optional.
33    ///
34    /// This is specifically used to determine when a field is inherently optional, such as a field
35    /// that is a true map like `HashMap<K, V>`. This doesn't apply to objects (i.e. structs)
36    /// because structs are implicitly non-optional: they have a fixed shape and size, and so on.
37    ///
38    /// Maps, by definition, are inherently free-form, and thus inherently optional. Thus, this
39    /// method should likely not be overridden except for implementing `Configurable` for map
40    /// types. If you're using it for something else, you are expected to know what you're doing.
41    fn is_optional() -> bool
42    where
43        Self: Sized,
44    {
45        false
46    }
47
48    /// Gets the metadata for this value.
49    fn metadata() -> Metadata
50    where
51        Self: Sized,
52    {
53        Metadata::default()
54    }
55
56    /// Validates the given metadata against this type.
57    ///
58    /// This allows for validating specific aspects of the given metadata, such as validation
59    /// bounds, and so on, to ensure they are valid for the given type. In some cases, such as with
60    /// numeric types, there is a limited amount of validation that can occur within the
61    /// `Configurable` derive macro, and additional validation must happen at runtime when the
62    /// `Configurable` trait is being used, which this method allows for.
63    fn validate_metadata(_metadata: &Metadata) -> Result<(), GenerateError>
64    where
65        Self: Sized,
66    {
67        Ok(())
68    }
69
70    /// Generates the schema for this value.
71    ///
72    /// # Errors
73    ///
74    /// If an error occurs while generating the schema, an error variant will be returned describing
75    /// the issue.
76    fn generate_schema(gen: &RefCell<SchemaGenerator>) -> Result<SchemaObject, GenerateError>
77    where
78        Self: Sized;
79
80    /// Create a new configurable reference table.
81    fn as_configurable_ref() -> ConfigurableRef
82    where
83        Self: Sized + 'static,
84    {
85        ConfigurableRef::new::<Self>()
86    }
87}
88
89/// A type that can be converted directly to a `serde_json::Value`. This is used when translating
90/// the default value in a `Metadata` into a schema object.
91pub trait ToValue {
92    /// Convert this value into a `serde_json::Value`. Must not fail.
93    fn to_value(&self) -> Value;
94}
95
96/// A pseudo-reference to a type that can be represented in a Vector configuration. This is
97/// composed of references to all the class trait functions.
98pub struct ConfigurableRef {
99    // TODO: Turn this into a plain value once this is resolved:
100    // https://github.com/rust-lang/rust/issues/63084
101    type_name: fn() -> &'static str,
102    // TODO: Turn this into a plain value once const trait functions are implemented
103    // Ref: https://github.com/rust-lang/rfcs/pull/911
104    referenceable_name: fn() -> Option<&'static str>,
105    make_metadata: fn() -> Metadata,
106    validate_metadata: fn(&Metadata) -> Result<(), GenerateError>,
107    generate_schema: fn(&RefCell<SchemaGenerator>) -> Result<SchemaObject, GenerateError>,
108}
109
110impl ConfigurableRef {
111    /// Create a new configurable reference table.
112    pub const fn new<T: Configurable + 'static>() -> Self {
113        Self {
114            type_name: std::any::type_name::<T>,
115            referenceable_name: T::referenceable_name,
116            make_metadata: T::metadata,
117            validate_metadata: T::validate_metadata,
118            generate_schema: T::generate_schema,
119        }
120    }
121
122    pub(crate) fn type_name(&self) -> &'static str {
123        (self.type_name)()
124    }
125    pub(crate) fn referenceable_name(&self) -> Option<&'static str> {
126        (self.referenceable_name)()
127    }
128    pub(crate) fn make_metadata(&self) -> Metadata {
129        (self.make_metadata)()
130    }
131    pub(crate) fn validate_metadata(&self, metadata: &Metadata) -> Result<(), GenerateError> {
132        (self.validate_metadata)(metadata)
133    }
134    pub(crate) fn generate_schema(
135        &self,
136        gen: &RefCell<SchemaGenerator>,
137    ) -> Result<SchemaObject, GenerateError> {
138        (self.generate_schema)(gen)
139    }
140}