struct_metadata/
lib.rs

1//! Macros for attaching metadata to structs.
2//!
3//! When a struct is used to define an interface with external systems there is
4//! often additonal information about the external system that is required.
5//! Rather than having that information stored separately this library
6//! intends to maintain a single source of truth.
7
8#![warn(missing_docs, non_ascii_idents, trivial_numeric_casts,
9    noop_method_call, single_use_lifetimes, trivial_casts,
10    unused_lifetimes, nonstandard_style, variant_size_differences)]
11#![deny(keyword_idents)]
12#![warn(clippy::missing_docs_in_private_items)]
13#![allow(clippy::needless_return, clippy::while_let_on_iterator)]
14
15pub use struct_metadata_derive::{Described, MetadataKind};
16
17use std::collections::HashMap;
18
19/// Information about a type along with its metadata and doc-strings.
20#[derive(Debug, PartialEq, Eq, Clone)]
21pub struct Descriptor<Metadata: Default> {
22    /// Docstring for the type
23    pub docs: Option<Vec<&'static str>>,
24    /// Metadata for the type
25    pub metadata: Metadata,
26    /// Details about the type
27    pub kind: Kind<Metadata>,
28}
29
30impl<Metadata: MetadataKind> Descriptor<Metadata> {
31    /// A helper method used by the Described derive macro
32    pub fn propagate(&mut self, context: Option<&Metadata> ) {
33        let context = match context {
34            Some(context) => {
35                self.metadata.forward_propagate_context(context);
36                context
37            },
38            None => {
39                &self.metadata
40            }
41        };
42        match &mut self.kind {
43            Kind::Struct { children, .. } => {
44                for child in children {
45                    child.metadata.forward_propagate_entry_defaults(context, &child.type_info.metadata);
46                    child.type_info.propagate(Some(&child.metadata));
47                    child.metadata.backward_propagate_entry_defaults(context, &child.type_info.metadata);
48                }
49            },
50            Kind::Aliased { kind, .. } |
51            Kind::Sequence(kind) | 
52            Kind::Option(kind) => {
53                self.metadata.forward_propagate_child_defaults(&kind.metadata);
54                kind.propagate(Some(&self.metadata));
55                self.metadata.backward_propagate_child_defaults(&kind.metadata);
56            },
57            // Kind::Enum { variants } => {
58                // for child in variants {
59                //     // child.metadata.forward_propagate_entry_defaults(&self.metadata, &child.type_info.metadata);
60                //     // child.type_info.propagate(Some(&child.metadata));
61                //     // child.metadata.backward_propagate_entry_defaults(&self.metadata, &child.type_info.metadata);
62                // }
63            // },
64            Kind::Mapping(key, value) => {
65                self.metadata.forward_propagate_child_defaults(&key.metadata);
66                self.metadata.forward_propagate_child_defaults(&value.metadata);
67                key.propagate(Some(&self.metadata));
68                value.propagate(Some(&self.metadata));
69                self.metadata.backward_propagate_child_defaults(&value.metadata);
70                self.metadata.backward_propagate_child_defaults(&key.metadata);
71
72            },
73            _ => {}
74        }
75    }
76
77    // fn propagate_internal(&mut self, context: &mut Metadata) {
78    //     match &mut self.kind {
79    //         Kind::Struct { name, children } => todo!(),
80    //         Kind::Aliased { name, kind } => todo!(),
81    //         Kind::Enum { name, variants } => todo!(),
82    //         Kind::Sequence(_) => todo!(),
83    //         Kind::Option(kind) => {
84    //             kind.p
85    //         },
86    //         Kind::Mapping(_, _) => todo!(),
87    //         _ => {}
88    //     }
89    // }
90}
91
92/// Enum reflecting all supported types
93#[derive(Debug, PartialEq, Eq, Clone)]
94#[non_exhaustive]
95pub enum Kind<Metadata: Default> {
96    /// The type is a struct
97    Struct {
98        /// Name given to the struct in its declaration
99        name: &'static str,
100        /// List of fields within this struct
101        children: Vec<Entry<Metadata>>,
102    },
103    /// A struct wrapping a single anonymous field
104    Aliased {
105        /// Name given to the struct in its declaration
106        name: &'static str,
107        /// The type this alias struct wraps
108        kind: Box<Descriptor<Metadata>>
109    },
110    /// A simple no-field enum type
111    Enum {
112        /// Name given to the enum in its declaration
113        name: &'static str,
114        /// Information about each variant value within this enum
115        variants: Vec<Variant<Metadata>>,
116    },
117    /// A list of items of a consistent type
118    Sequence( Box<Descriptor<Metadata>> ),
119    /// An item which is optionally present
120    Option( Box<Descriptor<Metadata>> ),
121    /// A pairwise mapping between consistent types with unique keys
122    Mapping( Box<Descriptor<Metadata>>, Box<Descriptor<Metadata>> ),
123    /// A field describing a point in time
124    DateTime,
125    /// A string
126    String,
127    /// Unsigned 128 bit integer
128    U128,
129    /// Signed 128 bit integer
130    I128,
131    /// Unsigned 64 bit integer
132    U64,
133    /// Signed 64 bit integer
134    I64,
135    /// Unsigned 32 bit integer
136    U32,
137    /// Signed 32 bit integer
138    I32,
139    /// Unsigned 16 bit integer
140    U16,
141    /// Signed 16 bit integer
142    I16,
143    /// Unsigned 8 bit integer
144    U8,
145    /// Signed 8 bit integer
146    I8,
147    /// 64 bit floating point number
148    F64,
149    /// 32 bit floating point number
150    F32,
151    /// A boolean value
152    Bool,
153    /// A value of unspecified type, that must be limited to json
154    JSON,
155    /// A value of unspecified type
156    Any,
157}
158
159impl<Metadata: MetadataKind> Kind<Metadata> {
160    /// Fetch the name of the type
161    pub fn name(&self) -> &'static str {
162        match self {
163            Kind::Struct { name, .. } => name,
164            Kind::Aliased { name, .. } => name,
165            Kind::Enum { name, .. } => name,
166            Kind::Sequence(_) => "sequence",
167            Kind::Option(_) => "option",
168            Kind::Mapping(_, _) => "mapping",
169            Kind::DateTime => "datetime",
170            Kind::String => "string",
171            Kind::U128 => "u128",
172            Kind::I128 => "i128",
173            Kind::U64 => "u64",
174            Kind::I64 => "i64",
175            Kind::U32 => "u32",
176            Kind::I32 => "i32",
177            Kind::U16 => "u16",
178            Kind::I16 => "i16",
179            Kind::U8 => "u8",
180            Kind::I8 => "i8",
181            Kind::F64 => "f64",
182            Kind::F32 => "f32",
183            Kind::Bool => "bool",
184            Kind::JSON => "json",
185            Kind::Any => "any",
186        }
187    }
188
189    /// Construct a type descriptor for a struct with the given name and fields.
190    /// 
191    /// Any structs in the flattened_children list will have their fields added to this
192    /// new struct as if they were members of it. (this corresponds to the 'flatten' parameter in serde)
193    pub fn new_struct(name: &'static str, mut children: Vec<Entry<Metadata>>, flattened_children: &mut [Descriptor<Metadata>], flattened_metadata: &mut [Metadata]) -> Self {
194        for (child, meta) in flattened_children.iter_mut().zip(flattened_metadata.iter_mut()) {
195            if let Kind::Struct { children: flattening, .. } = &mut child.kind {
196                for child in flattening.iter_mut() {
197                    child.metadata.forward_propagate_entry_defaults(meta, &child.type_info.metadata);
198                    child.type_info.propagate(Some(&child.metadata));
199                    child.metadata.backward_propagate_entry_defaults(meta, &child.type_info.metadata);
200                }
201                children.append(flattening)
202            }
203        }
204
205        Self::Struct { name, children }
206    }
207}
208
209/// Struct describing an enum variant
210#[derive(Debug, PartialEq, Eq, Clone)]
211pub struct Variant<Metadata: Default> {
212    /// String value used to describe the variant.
213    /// The DescribedEnumString derive can be used to build this label using the to_string method
214    pub label: &'static str,
215    /// doc strings describing this variant
216    pub docs: Option<Vec<&'static str>>,
217    /// metadata describing this variant
218    pub metadata: Metadata,
219    /// List of names this field may be known as
220    pub aliases: &'static [&'static str]
221}
222
223/// Struct describing a struct field
224#[derive(Debug, Clone)]
225pub struct Entry<Metadata: Default> {
226    /// Label of the field in question
227    /// This respects serde's rename attribute
228    pub label: &'static str,
229    /// doc string describing this field
230    pub docs: Option<Vec<&'static str>>,
231    /// metadata describing this field
232    pub metadata: Metadata,
233    /// Type of this field
234    pub type_info: Descriptor<Metadata>,
235    /// Wether this field has a default defined
236    pub has_default: bool,
237    /// List of names this field may be known as
238    pub aliases: &'static [&'static str]
239}
240
241impl<T: PartialEq + Default> PartialEq for Entry<T> {
242    fn eq(&self, other: &Self) -> bool {
243        self.label == other.label && self.docs == other.docs && self.metadata == other.metadata && self.type_info == other.type_info && self.has_default == other.has_default
244    }
245}
246
247impl<T: Eq + Default> Eq for Entry<T> {}
248
249/// A self description of the type being targeted including doc-strings and metadata annotations.
250pub trait Described<M: Default=HashMap<&'static str, &'static str>> {
251    /// Get self description of this type
252    fn metadata() -> Descriptor<M>;
253}
254
255/// Generate the simple formulaic implementation of Described for a basic type
256macro_rules! basic_described {
257    ($type_name:ident, $type_macro:ident) => {
258        impl<M: Default> Described<M> for $type_name {
259            fn metadata() -> Descriptor<M> { Descriptor { docs: None, metadata: M::default(), kind: Kind::$type_macro } }
260        }
261    };
262}
263
264basic_described!{String, String}
265basic_described!{i128, I128}
266basic_described!{u128, U128}
267basic_described!{i64, I64}
268basic_described!{u64, U64}
269basic_described!{i32, I32}
270basic_described!{u32, U32}
271basic_described!{i16, I16}
272basic_described!{u16, U16}
273basic_described!{i8, I8}
274basic_described!{u8, U8}
275basic_described!{f64, F64}
276basic_described!{f32, F32}
277basic_described!{bool, Bool}
278
279
280impl<M: Default, T: Described<M>> Described<M> for Option<T> {
281    fn metadata() -> Descriptor<M> {
282        Descriptor {
283            docs: None,
284            metadata: M::default(),
285            kind: Kind::Option(Box::new(T::metadata()))
286        }
287    }
288}
289
290#[cfg(feature = "std")]
291impl<M: Default, T: Described<M>> Described<M> for Box<T> {
292    fn metadata() -> Descriptor<M> { T::metadata() }
293}
294
295#[cfg(feature = "std")]
296impl<M: Default, T: Described<M>> Described<M> for Vec<T> {
297    fn metadata() -> Descriptor<M> {
298        Descriptor {
299            docs: None,
300            metadata: M::default(),
301            kind: Kind::Sequence(Box::new(T::metadata()))
302        }
303    }
304}
305
306#[cfg(feature = "std")]
307impl<M: Default, K: Described<M> + core::hash::Hash, V: Described<M>> Described<M> for HashMap<K, V> {
308    fn metadata() -> Descriptor<M> {
309        Descriptor {
310            docs: None,
311            metadata: M::default(),
312            kind: Kind::Mapping(Box::new(K::metadata()), Box::new(V::metadata()))
313        }
314    }
315}
316
317#[cfg(feature = "chrono")]
318impl<M: Default, Tz: chrono::TimeZone> Described<M> for chrono::DateTime<Tz> {
319    fn metadata() -> Descriptor<M> {
320        Descriptor { docs: None, metadata: M::default(), kind: Kind::DateTime }
321    }
322}
323
324#[cfg(feature = "serde_json")]
325impl<M: Default> Described<M> for serde_json::Value {
326    fn metadata() -> Descriptor<M> {
327        Descriptor { docs: None, metadata: M::default(), kind: Kind::JSON }
328    }
329}
330
331#[cfg(feature = "serde_json")]
332impl<M: Default, K: Described<M>, V: Described<M>> Described<M> for serde_json::Map<K, V> {
333    fn metadata() -> Descriptor<M> {
334        Descriptor {
335            docs: None,
336            metadata: M::default(),
337            kind: Kind::Mapping(Box::new(K::metadata()), Box::new(V::metadata()))
338        }
339    }
340}
341
342/// Trait used to describe metadata field propagation
343pub trait MetadataKind: Default {
344    /// Update metadata values on nested contexts generally
345    fn forward_propagate_context(&mut self, _context: &Self) {}
346    /// Update metadata values on an entry based on the outer context and inner type data
347    fn forward_propagate_entry_defaults(&mut self, _context: &Self, _kind: &Self) {}
348    /// Update metadata values on an entry based on the outer context and inner type data
349    fn backward_propagate_entry_defaults(&mut self, _context: &Self, _kind: &Self) {}
350    /// Update metadata values on a type entry based on its child type 
351    fn forward_propagate_child_defaults(&mut self, _kind: &Self) {}
352    /// Update metadata values on a type entry based on its child type 
353    fn backward_propagate_child_defaults(&mut self, _kind: &Self) {}
354}
355
356impl<K, V> MetadataKind for HashMap<K, V> {}
357impl<V> MetadataKind for Vec<V> {}