1use super::error::{ParameterError, ParameterResult};
10use std::collections::HashMap;
11use std::fmt;
12use std::str::FromStr;
13
14#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
20pub struct ParameterId {
21 name: String,
22}
23
24impl ParameterId {
25 pub const MAX_LEN: usize = 64;
27
28 pub fn new(name: impl Into<String>) -> ParameterResult<Self> {
36 let name = name.into();
37
38 if name.is_empty() {
39 return Err(ParameterError::Empty);
40 }
41
42 if name.len() > Self::MAX_LEN {
43 return Err(ParameterError::TooLong { max: Self::MAX_LEN });
44 }
45
46 let first = name.chars().next().unwrap();
47 if !first.is_ascii_alphabetic() {
48 return Err(ParameterError::MustStartWithLetter);
49 }
50
51 for c in name.chars() {
52 if !c.is_ascii_alphanumeric() && c != '_' {
53 return Err(ParameterError::InvalidCharacter(c));
54 }
55 }
56
57 Ok(Self { name })
58 }
59
60 pub fn as_str(&self) -> &str {
62 &self.name
63 }
64
65 pub fn into_string(self) -> String {
67 self.name
68 }
69}
70
71impl AsRef<str> for ParameterId {
72 fn as_ref(&self) -> &str {
73 &self.name
74 }
75}
76
77impl fmt::Display for ParameterId {
78 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79 write!(f, "{}", self.name)
80 }
81}
82
83impl FromStr for ParameterId {
84 type Err = ParameterError;
85
86 fn from_str(s: &str) -> Result<Self, Self::Err> {
87 ParameterId::new(s)
88 }
89}
90
91#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
97pub enum ParamType {
98 Float,
100
101 Int,
103
104 Bool,
106
107 String,
109
110 Choice,
112
113 Bytes,
115}
116
117impl ParamType {
118 pub fn name(&self) -> &'static str {
120 match self {
121 Self::Float => "float",
122 Self::Int => "int",
123 Self::Bool => "bool",
124 Self::String => "string",
125 Self::Choice => "choice",
126 Self::Bytes => "bytes",
127 }
128 }
129}
130
131impl fmt::Display for ParamType {
132 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133 write!(f, "{}", self.name())
134 }
135}
136
137#[derive(Debug, Clone, PartialEq)]
143#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
144pub enum ParamValue {
145 Float(f32),
147
148 Int(i32),
150
151 Bool(bool),
153
154 String(String),
156
157 Choice(String),
159
160 Bytes(Vec<u8>),
162}
163
164impl ParamValue {
165 pub fn param_type(&self) -> ParamType {
167 match self {
168 Self::Float(_) => ParamType::Float,
169 Self::Int(_) => ParamType::Int,
170 Self::Bool(_) => ParamType::Bool,
171 Self::String(_) => ParamType::String,
172 Self::Choice(_) => ParamType::Choice,
173 Self::Bytes(_) => ParamType::Bytes,
174 }
175 }
176
177 pub fn as_f32(&self) -> Option<f32> {
179 match self {
180 Self::Float(f) => Some(*f),
181 Self::Int(i) => Some(*i as f32),
182 Self::Bool(b) => Some(if *b { 1.0 } else { 0.0 }),
183 _ => None,
184 }
185 }
186
187 pub fn as_i32(&self) -> Option<i32> {
189 match self {
190 Self::Float(f) => Some(*f as i32),
191 Self::Int(i) => Some(*i),
192 Self::Bool(b) => Some(if *b { 1 } else { 0 }),
193 _ => None,
194 }
195 }
196
197 pub fn as_bool(&self) -> Option<bool> {
199 match self {
200 Self::Bool(b) => Some(*b),
201 Self::Float(f) => Some(*f > 0.5),
202 Self::Int(i) => Some(*i > 0),
203 _ => None,
204 }
205 }
206
207 pub fn as_str(&self) -> Option<&str> {
209 match self {
210 Self::String(s) | Self::Choice(s) => Some(s.as_str()),
211 _ => None,
212 }
213 }
214
215 pub fn as_bytes(&self) -> Option<&[u8]> {
217 match self {
218 Self::Bytes(v) => Some(v.as_slice()),
219 _ => None,
220 }
221 }
222}
223
224#[derive(Debug, Clone, PartialEq)]
230pub struct ParamRange {
231 pub min: Option<f32>,
233
234 pub max: Option<f32>,
236
237 pub step: Option<f32>,
239}
240
241impl ParamRange {
242 pub fn new() -> Self {
244 Self {
245 min: None,
246 max: None,
247 step: None,
248 }
249 }
250
251 pub fn with_min(mut self, min: f32) -> Self {
253 self.min = Some(min);
254 self
255 }
256
257 pub fn with_max(mut self, max: f32) -> Self {
259 self.max = Some(max);
260 self
261 }
262
263 pub fn with_step(mut self, step: f32) -> Self {
265 self.step = Some(step);
266 self
267 }
268
269 pub fn contains(&self, value: f32) -> bool {
271 if let Some(min) = self.min {
272 if value < min {
273 return false;
274 }
275 }
276 if let Some(max) = self.max {
277 if value > max {
278 return false;
279 }
280 }
281 true
282 }
283
284 pub fn clamp(&self, value: f32) -> f32 {
286 let mut value = value;
287 if let Some(min) = self.min {
288 value = value.max(min);
289 }
290 if let Some(max) = self.max {
291 value = value.min(max);
292 }
293 value
294 }
295}
296
297impl Default for ParamRange {
298 fn default() -> Self {
299 Self::new()
300 }
301}
302
303#[derive(Debug, Clone, PartialEq)]
309pub struct ParamMetadata {
310 pub name: String,
312
313 pub description: String,
315
316 pub typ: ParamType,
318
319 pub default: ParamValue,
321
322 pub range: ParamRange,
324
325 pub unit: Option<String>,
327
328 pub choices: Option<Vec<(String, f32)>>,
330}
331
332impl ParamMetadata {
333 pub fn new(name: impl Into<String>, typ: ParamType, default: ParamValue) -> Self {
335 Self {
336 name: name.into(),
337 description: String::new(),
338 typ,
339 default,
340 range: ParamRange::default(),
341 unit: None,
342 choices: None,
343 }
344 }
345
346 pub fn with_description(mut self, description: impl Into<String>) -> Self {
348 self.description = description.into();
349 self
350 }
351
352 pub fn with_range(mut self, min: f32, max: f32, step: f32) -> Self {
354 self.range = ParamRange::new()
355 .with_min(min)
356 .with_max(max)
357 .with_step(step);
358 self
359 }
360
361 pub fn with_unit(mut self, unit: impl Into<String>) -> Self {
363 self.unit = Some(unit.into());
364 self
365 }
366
367 pub fn with_choices(mut self, choices: Vec<(String, f32)>) -> Self {
369 self.choices = Some(choices);
370 self
371 }
372}
373
374#[derive(Debug, Clone, Default)]
386#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
387pub struct Params {
388 pub sample_rate: f32,
390
391 pub parameters: HashMap<String, ParamValue>,
393}
394
395impl Params {
396 pub fn new(sample_rate: f32) -> Self {
398 Self {
399 sample_rate,
400 parameters: HashMap::new(),
401 }
402 }
403
404 pub fn with(mut self, key: impl Into<String>, value: ParamValue) -> Self {
406 self.parameters.insert(key.into(), value);
407 self
408 }
409
410 pub fn get(&self, key: &str) -> Option<&ParamValue> {
412 self.parameters.get(key)
413 }
414
415 pub fn insert(&mut self, key: impl Into<String>, value: ParamValue) -> Option<ParamValue> {
417 self.parameters.insert(key.into(), value)
418 }
419
420 pub fn remove(&mut self, key: &str) -> Option<ParamValue> {
422 self.parameters.remove(key)
423 }
424
425 pub fn contains(&self, key: &str) -> bool {
427 self.parameters.contains_key(key)
428 }
429
430 pub fn len(&self) -> usize {
432 self.parameters.len()
433 }
434
435 pub fn is_empty(&self) -> bool {
437 self.parameters.is_empty()
438 }
439
440 pub fn get_f32(&self, key: &str, default: f32) -> f32 {
442 self.parameters
443 .get(key)
444 .and_then(|v| v.as_f32())
445 .unwrap_or(default)
446 }
447
448 pub fn get_i32(&self, key: &str, default: i32) -> i32 {
450 self.parameters
451 .get(key)
452 .and_then(|v| v.as_i32())
453 .unwrap_or(default)
454 }
455
456 pub fn get_bool(&self, key: &str, default: bool) -> bool {
458 self.parameters
459 .get(key)
460 .and_then(|v| v.as_bool())
461 .unwrap_or(default)
462 }
463}
464
465#[cfg(test)]
470mod tests {
471 use super::*;
472
473 #[test]
474 fn test_parameter_id_valid() {
475 assert!(ParameterId::new("gain").is_ok());
476 assert!(ParameterId::new("cutoff_freq").is_ok());
477 assert!(ParameterId::new("delay_time_2").is_ok());
478 }
479
480 #[test]
481 fn test_parameter_id_invalid() {
482 assert!(ParameterId::new("").is_err());
483 assert!(ParameterId::new("1gain").is_err());
484 assert!(ParameterId::new("_gain").is_err());
485
486 let long_name = "a".repeat(ParameterId::MAX_LEN + 1);
487 assert!(ParameterId::new(long_name).is_err());
488 }
489
490 #[test]
491 fn test_param_value_conversion() {
492 let f = ParamValue::Float(42.0);
493 assert_eq!(f.as_f32(), Some(42.0));
494 assert_eq!(f.as_i32(), Some(42));
495 assert_eq!(f.as_bool(), Some(true));
496
497 let i = ParamValue::Int(0);
498 assert_eq!(i.as_f32(), Some(0.0));
499 assert_eq!(i.as_i32(), Some(0));
500 assert_eq!(i.as_bool(), Some(false));
501 }
502}