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}