Skip to main content

rust_config_tree/
transparent_section.rs

1//! Transparent array section wrappers for split-friendly configuration.
2//!
3//! Sections marked with `x-tree-transparent-array` serialize as YAML arrays in
4//! single-file configs and as body-only arrays in split section files.
5
6use confique::serde::{Deserialize, Deserializer, Serialize, Serializer};
7use schemars::{JsonSchema, Schema, SchemaGenerator};
8use std::borrow::Cow;
9
10/// Split-friendly nested section that transparently serializes as an array.
11#[derive(Debug, Clone, PartialEq, Default)]
12pub struct ArraySection<T> {
13    /// Inner items loaded from the transparent section body.
14    pub items: Vec<T>,
15}
16
17impl<T> ArraySection<T> {
18    /// Returns inner items as a slice.
19    pub fn as_slice(&self) -> &[T] {
20        &self.items
21    }
22
23    /// Returns the number of inner items.
24    pub fn len(&self) -> usize {
25        self.items.len()
26    }
27
28    /// Returns whether this section contains no items.
29    pub fn is_empty(&self) -> bool {
30        self.items.is_empty()
31    }
32}
33
34impl<T> From<ArraySection<T>> for Vec<T> {
35    fn from(section: ArraySection<T>) -> Self {
36        section.items
37    }
38}
39
40impl<T: JsonSchema> JsonSchema for ArraySection<T> {
41    fn schema_name() -> Cow<'static, str> {
42        Cow::Borrowed("ArraySection")
43    }
44
45    fn json_schema(generator: &mut SchemaGenerator) -> Schema {
46        Vec::<T>::json_schema(generator)
47    }
48}
49
50impl<T: Serialize> Serialize for ArraySection<T> {
51    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
52    where
53        S: Serializer,
54    {
55        self.items.serialize(serializer)
56    }
57}
58
59impl<'de, T: Deserialize<'de>> Deserialize<'de> for ArraySection<T> {
60    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
61    where
62        D: Deserializer<'de>,
63    {
64        Ok(Self {
65            items: Vec::<T>::deserialize(deserializer)?,
66        })
67    }
68}
69
70/// Generates a transparent array section wrapper with confique support.
71///
72/// # Examples
73///
74/// ```ignore
75/// transparent_array_section! {
76///     /// Child declarations loaded from YAML.
77///     pub struct ChildrenConfigSection {
78///         #[config(default = [{ "name": "worker" }])]
79///         pub items: Vec<ChildDeclaration>,
80///     }
81/// }
82/// ```
83#[macro_export]
84macro_rules! transparent_array_section {
85    (
86        $(#[$struct_meta:meta])*
87        $vis:vis struct $name:ident {
88            $(#[$items_meta:meta])*
89            $items_vis:vis items: Vec<$item:ty> $(,)?
90        }
91    ) => {
92        $(#[$struct_meta])*
93        #[derive(Debug, Clone, PartialEq, confique::Config)]
94        $vis struct $name {
95            $(#[$items_meta])*
96            $items_vis items: Vec<$item>,
97        }
98
99        impl Default for $name {
100            fn default() -> Self {
101                Self { items: Vec::new() }
102            }
103        }
104
105        impl schemars::JsonSchema for $name {
106            fn schema_name() -> std::borrow::Cow<'static, str> {
107                std::borrow::Cow::Borrowed(stringify!($name))
108            }
109
110            fn json_schema(
111                generator: &mut schemars::SchemaGenerator,
112            ) -> schemars::Schema {
113                Vec::<$item>::json_schema(generator)
114            }
115        }
116
117        impl serde::Serialize for $name {
118            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
119            where
120                S: confique::serde::Serializer,
121            {
122                self.items.serialize(serializer)
123            }
124        }
125
126        impl<'de> serde::Deserialize<'de> for $name {
127            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
128            where
129                D: confique::serde::Deserializer<'de>,
130            {
131                Ok(Self {
132                    items: Vec::<$item>::deserialize(deserializer)?,
133                })
134            }
135        }
136
137        impl $name {
138            /// Returns inner items as a slice.
139            pub fn as_slice(&self) -> &[$item] {
140                &self.items
141            }
142
143            /// Returns the number of inner items.
144            pub fn len(&self) -> usize {
145                self.items.len()
146            }
147
148            /// Returns whether this section contains no items.
149            pub fn is_empty(&self) -> bool {
150                self.items.is_empty()
151            }
152        }
153
154        impl From<$name> for Vec<$item> {
155            fn from(section: $name) -> Self {
156                section.items
157            }
158        }
159    };
160}