1#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
12#[cfg_attr(feature = "schema", derive(utoipa::ToSchema))]
13#[cfg_attr(feature = "schema", schema(value_type = u32))]
14pub struct ControlId(pub u32);
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
19#[cfg_attr(feature = "schema", derive(utoipa::ToSchema))]
20pub enum Access {
21 ReadOnly,
22 ReadWrite,
23}
24
25#[derive(Debug, Clone)]
45#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
46#[cfg_attr(feature = "schema", derive(utoipa::ToSchema))]
47pub struct ControlMeta {
48 pub id: ControlId,
50 pub name: String,
52 pub kind: ControlKind,
54 pub access: Access,
56 pub min: ControlValue,
58 pub max: ControlValue,
60 pub default: ControlValue,
62 #[cfg_attr(feature = "serde", serde(default))]
64 pub step: Option<ControlValue>,
65 #[cfg_attr(feature = "serde", serde(default))]
67 pub menu: Option<Vec<String>>,
68}
69
70#[derive(Debug, Clone, Copy, PartialEq, Eq)]
72#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
73#[cfg_attr(feature = "schema", derive(utoipa::ToSchema))]
74pub enum ControlKind {
75 None,
76 Bool,
77 Int,
78 Uint,
79 Float,
80 Menu,
81 IntMenu,
82 Unknown,
83}
84
85#[derive(Debug, Clone, PartialEq)]
95#[cfg_attr(feature = "schema", derive(utoipa::ToSchema))]
96pub enum ControlValue {
97 None,
99 Bool(bool),
101 Int(i32),
103 Uint(u32),
105 Float(f32),
107}
108
109#[cfg(feature = "serde")]
110impl serde::Serialize for ControlValue {
111 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
112 where
113 S: serde::Serializer,
114 {
115 if serializer.is_human_readable() {
116 #[derive(serde::Serialize)]
117 #[serde(tag = "kind", content = "value", rename_all = "snake_case")]
118 enum Human {
119 None,
120 Bool(bool),
121 Int(i32),
122 Uint(u32),
123 Float(f32),
124 }
125 let h = match self {
126 ControlValue::None => Human::None,
127 ControlValue::Bool(v) => Human::Bool(*v),
128 ControlValue::Int(v) => Human::Int(*v),
129 ControlValue::Uint(v) => Human::Uint(*v),
130 ControlValue::Float(v) => Human::Float(*v),
131 };
132 h.serialize(serializer)
133 } else {
134 #[derive(serde::Serialize)]
135 enum Binary {
136 None,
137 Bool(bool),
138 Int(i32),
139 Uint(u32),
140 Float(f32),
141 }
142 let b = match self {
143 ControlValue::None => Binary::None,
144 ControlValue::Bool(v) => Binary::Bool(*v),
145 ControlValue::Int(v) => Binary::Int(*v),
146 ControlValue::Uint(v) => Binary::Uint(*v),
147 ControlValue::Float(v) => Binary::Float(*v),
148 };
149 b.serialize(serializer)
150 }
151 }
152}
153
154#[cfg(feature = "serde")]
155impl<'de> serde::Deserialize<'de> for ControlValue {
156 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
157 where
158 D: serde::Deserializer<'de>,
159 {
160 if deserializer.is_human_readable() {
161 #[derive(serde::Deserialize)]
162 #[serde(tag = "kind", content = "value", rename_all = "snake_case")]
163 enum Human {
164 None,
165 Bool(bool),
166 Int(i32),
167 Uint(u32),
168 Float(f32),
169 }
170 let h = Human::deserialize(deserializer)?;
171 Ok(match h {
172 Human::None => ControlValue::None,
173 Human::Bool(v) => ControlValue::Bool(v),
174 Human::Int(v) => ControlValue::Int(v),
175 Human::Uint(v) => ControlValue::Uint(v),
176 Human::Float(v) => ControlValue::Float(v),
177 })
178 } else {
179 #[derive(serde::Deserialize)]
180 enum Binary {
181 None,
182 Bool(bool),
183 Int(i32),
184 Uint(u32),
185 Float(f32),
186 }
187 let b = Binary::deserialize(deserializer)?;
188 Ok(match b {
189 Binary::None => ControlValue::None,
190 Binary::Bool(v) => ControlValue::Bool(v),
191 Binary::Int(v) => ControlValue::Int(v),
192 Binary::Uint(v) => ControlValue::Uint(v),
193 Binary::Float(v) => ControlValue::Float(v),
194 })
195 }
196 }
197}
198
199impl ControlMeta {
200 pub fn validate(&self, candidate: &ControlValue) -> bool {
220 if let Some(menu) = &self.menu {
221 if let ControlValue::Uint(idx) = candidate {
223 return (*idx as usize) < menu.len();
224 }
225 if let ControlValue::Int(idx) = candidate {
226 return (*idx >= 0) && ((*idx as usize) < menu.len());
227 }
228 }
229
230 match (candidate, &self.min, &self.max) {
231 (ControlValue::Bool(v), ControlValue::Bool(min), ControlValue::Bool(max)) => {
232 let v = *v as u8;
233 let min = *min as u8;
234 let max = *max as u8;
235 v >= min && v <= max
236 }
237 (ControlValue::Int(v), ControlValue::Int(min), ControlValue::Int(max)) => {
238 let within = v >= min && v <= max;
239 if !within {
240 return false;
241 }
242 if let Some(ControlValue::Int(step)) = &self.step
243 && *step > 0
244 {
245 return ((v - min) % step) == 0;
246 }
247 true
248 }
249 (ControlValue::Uint(v), ControlValue::Uint(min), ControlValue::Uint(max)) => {
250 let within = v >= min && v <= max;
251 if !within {
252 return false;
253 }
254 if let Some(ControlValue::Uint(step)) = &self.step
255 && *step > 0
256 {
257 return ((v - min) % step) == 0;
258 }
259 true
260 }
261 (ControlValue::Float(v), ControlValue::Float(min), ControlValue::Float(max)) => {
262 v >= min && v <= max
263 }
264 (ControlValue::None, ControlValue::None, ControlValue::None) => true,
265 _ => false,
266 }
267 }
268}