nemo_flow/
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 Enum,
23 StringMap,
25 Json,
27 Section,
29}
30
31#[derive(Clone, Copy)]
33pub struct EditorFieldSpec {
34 pub name: &'static str,
36 pub label: &'static str,
38 pub kind: EditorFieldKind,
40 pub enum_values: &'static [&'static str],
42 pub optional: bool,
44 pub nested_schema: Option<fn() -> &'static EditorSchema>,
46 pub nested_default: Option<fn() -> Json>,
48}
49
50impl EditorFieldSpec {
51 pub fn schema(self) -> Option<&'static EditorSchema> {
53 self.nested_schema.map(|schema| schema())
54 }
55
56 pub fn default_value(self) -> Option<Json> {
58 self.nested_default.map(|default_value| default_value())
59 }
60}
61
62#[derive(Clone, Copy)]
64pub struct EditorSchema {
65 pub fields: &'static [EditorFieldSpec],
67}
68
69impl EditorSchema {
70 pub fn field(self, name: &str) -> Option<EditorFieldSpec> {
72 self.fields.iter().copied().find(|field| field.name == name)
73 }
74}
75
76pub trait EditorConfig {
78 fn editor_schema() -> &'static EditorSchema;
80}
81
82#[macro_export]
88macro_rules! editor_config {
89 (
90 impl $ty:ty {
91 $(
92 $field:ident => {
93 label: $label:literal,
94 kind: $kind:ident
95 $(, values: [$($value:literal),* $(,)?])?
96 $(, optional: $optional:literal)?
97 $(, nested: $nested:ty)?
98 $(, default: $default:ty)?
99 $(,)?
100 }
101 ),* $(,)?
102 }
103 ) => {
104 const _: fn(&$ty) = |value: &$ty| {
105 $(
106 let _ = &value.$field;
107 )*
108 };
109
110 impl $crate::config_editor::EditorConfig for $ty {
111 fn editor_schema() -> &'static $crate::config_editor::EditorSchema {
112 static SCHEMA: $crate::config_editor::EditorSchema = $crate::config_editor::EditorSchema {
113 fields: &[
114 $(
115 $crate::config_editor::EditorFieldSpec {
116 name: stringify!($field),
117 label: $label,
118 kind: $crate::editor_config!(@kind $kind),
119 enum_values: $crate::editor_config!(@values $($($value),*)?),
120 optional: $crate::editor_config!(@optional $($optional)?),
121 nested_schema: $crate::editor_config!(@nested $($nested)?),
122 nested_default: $crate::editor_config!(@default $($default)?),
123 }
124 ),*
125 ],
126 };
127 &SCHEMA
128 }
129 }
130 };
131
132 (@kind Boolean) => { $crate::config_editor::EditorFieldKind::Boolean };
133 (@kind String) => { $crate::config_editor::EditorFieldKind::String };
134 (@kind Integer) => { $crate::config_editor::EditorFieldKind::Integer };
135 (@kind Enum) => { $crate::config_editor::EditorFieldKind::Enum };
136 (@kind StringMap) => { $crate::config_editor::EditorFieldKind::StringMap };
137 (@kind Json) => { $crate::config_editor::EditorFieldKind::Json };
138 (@kind Section) => { $crate::config_editor::EditorFieldKind::Section };
139
140 (@values) => { &[] };
141 (@values $($value:literal),*) => { &[$($value),*] };
142
143 (@optional) => { false };
144 (@optional $optional:literal) => { $optional };
145
146 (@nested) => { None };
147 (@nested $nested:ty) => {
148 Some(<$nested as $crate::config_editor::EditorConfig>::editor_schema)
149 };
150
151 (@default) => { None };
152 (@default $default:ty) => {
153 Some(|| {
154 serde_json::to_value(<$default as Default>::default())
155 .expect("editor default value should serialize")
156 })
157 };
158}