nemo_relay/
config_editor.rs1use serde_json::Value as Json;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum EditorFieldKind {
15 Boolean,
17 String,
19 Integer,
21 Float,
23 Enum,
25 StringMap,
27 Json,
29 List,
31 TaggedUnion,
33 Section,
35}
36
37#[derive(Clone, Copy)]
39pub struct EditorFieldSpec {
40 pub name: &'static str,
42 pub label: &'static str,
44 pub kind: EditorFieldKind,
46 pub enum_values: &'static [&'static str],
48 pub optional: bool,
50 pub nested_schema: Option<fn() -> &'static EditorSchema>,
52 pub nested_default: Option<fn() -> Json>,
54 pub list_item: Option<&'static EditorListItemSpec>,
56 pub tagged_union: Option<&'static EditorTaggedUnionSpec>,
58}
59
60#[derive(Clone, Copy)]
62pub struct EditorTaggedUnionSpec {
63 pub discriminator: &'static str,
65 pub variants: &'static [EditorVariantSpec],
67}
68
69#[derive(Clone, Copy)]
71pub struct EditorListItemSpec {
72 pub kind: EditorFieldKind,
74 pub schema: Option<fn() -> &'static EditorSchema>,
76 pub default: Option<fn() -> Json>,
78 pub tagged_union: Option<&'static EditorTaggedUnionSpec>,
80 pub list_item: Option<&'static EditorListItemSpec>,
82}
83
84#[derive(Clone, Copy)]
86pub struct EditorVariantSpec {
87 pub label: &'static str,
89 pub tag: &'static str,
91 pub schema: fn() -> &'static EditorSchema,
93 pub default: fn() -> Json,
95}
96
97pub fn default_string_list_item_value() -> Json {
99 Json::String(String::new())
100}
101
102pub fn default_json_list_item_value() -> Json {
104 Json::Null
105}
106
107pub static STRING_LIST_ITEM: EditorListItemSpec = EditorListItemSpec {
109 kind: EditorFieldKind::String,
110 schema: None,
111 default: Some(default_string_list_item_value),
112 tagged_union: None,
113 list_item: None,
114};
115
116pub static JSON_LIST_ITEM: EditorListItemSpec = EditorListItemSpec {
118 kind: EditorFieldKind::Json,
119 schema: None,
120 default: Some(default_json_list_item_value),
121 tagged_union: None,
122 list_item: None,
123};
124
125impl EditorFieldSpec {
126 pub fn schema(self) -> Option<&'static EditorSchema> {
128 self.nested_schema.map(|schema| schema())
129 }
130
131 pub fn default_value(self) -> Option<Json> {
133 self.nested_default.map(|default_value| default_value())
134 }
135}
136
137#[derive(Clone, Copy)]
139pub struct EditorSchema {
140 pub fields: &'static [EditorFieldSpec],
142}
143
144impl EditorSchema {
145 pub fn field(self, name: &str) -> Option<EditorFieldSpec> {
147 self.fields.iter().copied().find(|field| field.name == name)
148 }
149}
150
151pub trait EditorConfig {
153 fn editor_schema() -> &'static EditorSchema;
155}
156
157#[macro_export]
163macro_rules! editor_config {
164 (
165 impl $ty:ty {
166 $(
167 $field:ident => {
168 label: $label:literal,
169 kind: $kind:ident
170 $(, values: [$($value:literal),* $(,)?])?
171 $(, optional: $optional:literal)?
172 $(, nested: $nested:ty)?
173 $(, default: $default:ty)?
174 $(, list: $list:expr)?
175 $(, tagged_union: $tagged_union:expr)?
176 $(,)?
177 }
178 ),* $(,)?
179 }
180 ) => {
181 const _: fn(&$ty) = |value: &$ty| {
182 $(
183 let _ = &value.$field;
184 )*
185 };
186
187 impl $crate::config_editor::EditorConfig for $ty {
188 fn editor_schema() -> &'static $crate::config_editor::EditorSchema {
189 static SCHEMA: $crate::config_editor::EditorSchema = $crate::config_editor::EditorSchema {
190 fields: &[
191 $(
192 $crate::config_editor::EditorFieldSpec {
193 name: stringify!($field),
194 label: $label,
195 kind: $crate::editor_config!(@kind $kind),
196 enum_values: $crate::editor_config!(@values $($($value),*)?),
197 optional: $crate::editor_config!(@optional $($optional)?),
198 nested_schema: $crate::editor_config!(@nested $($nested)?),
199 nested_default: $crate::editor_config!(@default $($default)?),
200 list_item: $crate::editor_config!(@list $($list)?),
201 tagged_union: $crate::editor_config!(@tagged_union $($tagged_union)?),
202 }
203 ),*
204 ],
205 };
206 &SCHEMA
207 }
208 }
209 };
210
211 (@kind Boolean) => { $crate::config_editor::EditorFieldKind::Boolean };
212 (@kind String) => { $crate::config_editor::EditorFieldKind::String };
213 (@kind Integer) => { $crate::config_editor::EditorFieldKind::Integer };
214 (@kind Float) => { $crate::config_editor::EditorFieldKind::Float };
215 (@kind Enum) => { $crate::config_editor::EditorFieldKind::Enum };
216 (@kind StringMap) => { $crate::config_editor::EditorFieldKind::StringMap };
217 (@kind Json) => { $crate::config_editor::EditorFieldKind::Json };
218 (@kind List) => { $crate::config_editor::EditorFieldKind::List };
219 (@kind TaggedUnion) => { $crate::config_editor::EditorFieldKind::TaggedUnion };
220 (@kind Section) => { $crate::config_editor::EditorFieldKind::Section };
221
222 (@values) => { &[] };
223 (@values $($value:literal),*) => { &[$($value),*] };
224
225 (@optional) => { false };
226 (@optional $optional:literal) => { $optional };
227
228 (@nested) => { None };
229 (@nested $nested:ty) => {
230 Some(<$nested as $crate::config_editor::EditorConfig>::editor_schema)
231 };
232
233 (@default) => { None };
234 (@default $default:ty) => {
235 Some(|| {
236 serde_json::to_value(<$default as Default>::default())
237 .expect("editor default value should serialize")
238 })
239 };
240
241 (@list) => { None };
242 (@list $list:expr) => { Some($list) };
243
244 (@tagged_union) => { None };
245 (@tagged_union $tagged_union:expr) => { Some($tagged_union) };
246}