1use super::error::{ParameterError, ParameterResult};
7use std::fmt;
8use std::str::FromStr;
9
10#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
16pub struct ParameterId {
17 name: String,
18}
19
20impl ParameterId {
21 pub const MAX_LEN: usize = 64;
23
24 pub fn new(name: impl Into<String>) -> ParameterResult<Self> {
32 let name = name.into();
33
34 if name.is_empty() {
35 return Err(ParameterError::Empty);
36 }
37
38 if name.len() > Self::MAX_LEN {
39 return Err(ParameterError::TooLong { max: Self::MAX_LEN });
40 }
41
42 let first = name.chars().next().unwrap();
43 if !first.is_ascii_alphabetic() {
44 return Err(ParameterError::MustStartWithLetter);
45 }
46
47 for c in name.chars() {
48 if !c.is_ascii_alphanumeric() && c != '_' {
49 return Err(ParameterError::InvalidCharacter(c));
50 }
51 }
52
53 Ok(Self { name })
54 }
55
56 pub fn as_str(&self) -> &str {
58 &self.name
59 }
60
61 pub fn into_string(self) -> String {
63 self.name
64 }
65}
66
67impl AsRef<str> for ParameterId {
68 fn as_ref(&self) -> &str {
69 &self.name
70 }
71}
72
73impl fmt::Display for ParameterId {
74 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75 write!(f, "{}", self.name)
76 }
77}
78
79impl FromStr for ParameterId {
80 type Err = ParameterError;
81
82 fn from_str(s: &str) -> Result<Self, Self::Err> {
83 ParameterId::new(s)
84 }
85}
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
93pub enum ParamType {
94 Float,
96
97 Int,
99
100 Bool,
102
103 String,
105
106 Choice,
108}
109
110impl ParamType {
111 pub fn name(&self) -> &'static str {
113 match self {
114 Self::Float => "float",
115 Self::Int => "int",
116 Self::Bool => "bool",
117 Self::String => "string",
118 Self::Choice => "choice",
119 }
120 }
121}
122
123impl fmt::Display for ParamType {
124 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125 write!(f, "{}", self.name())
126 }
127}
128
129#[derive(Debug, Clone, PartialEq)]
135pub enum ParamValue {
136 Float(f32),
138
139 Int(i32),
141
142 Bool(bool),
144
145 String(String),
147
148 Choice(String),
150}
151
152impl ParamValue {
153 pub fn param_type(&self) -> ParamType {
155 match self {
156 Self::Float(_) => ParamType::Float,
157 Self::Int(_) => ParamType::Int,
158 Self::Bool(_) => ParamType::Bool,
159 Self::String(_) => ParamType::String,
160 Self::Choice(_) => ParamType::Choice,
161 }
162 }
163
164 pub fn as_f32(&self) -> Option<f32> {
166 match self {
167 Self::Float(f) => Some(*f),
168 Self::Int(i) => Some(*i as f32),
169 Self::Bool(b) => Some(if *b { 1.0 } else { 0.0 }),
170 _ => None,
171 }
172 }
173
174 pub fn as_i32(&self) -> Option<i32> {
176 match self {
177 Self::Float(f) => Some(*f as i32),
178 Self::Int(i) => Some(*i),
179 Self::Bool(b) => Some(if *b { 1 } else { 0 }),
180 _ => None,
181 }
182 }
183
184 pub fn as_bool(&self) -> Option<bool> {
186 match self {
187 Self::Bool(b) => Some(*b),
188 Self::Float(f) => Some(*f > 0.5),
189 Self::Int(i) => Some(*i > 0),
190 _ => None,
191 }
192 }
193}
194
195#[derive(Debug, Clone, PartialEq)]
201pub struct ParamRange {
202 pub min: Option<f32>,
204
205 pub max: Option<f32>,
207
208 pub step: Option<f32>,
210}
211
212impl ParamRange {
213 pub fn new() -> Self {
215 Self {
216 min: None,
217 max: None,
218 step: None,
219 }
220 }
221
222 pub fn with_min(mut self, min: f32) -> Self {
224 self.min = Some(min);
225 self
226 }
227
228 pub fn with_max(mut self, max: f32) -> Self {
230 self.max = Some(max);
231 self
232 }
233
234 pub fn with_step(mut self, step: f32) -> Self {
236 self.step = Some(step);
237 self
238 }
239
240 pub fn contains(&self, value: f32) -> bool {
242 if let Some(min) = self.min {
243 if value < min {
244 return false;
245 }
246 }
247 if let Some(max) = self.max {
248 if value > max {
249 return false;
250 }
251 }
252 true
253 }
254
255 pub fn clamp(&self, value: f32) -> f32 {
257 let mut value = value;
258 if let Some(min) = self.min {
259 value = value.max(min);
260 }
261 if let Some(max) = self.max {
262 value = value.min(max);
263 }
264 value
265 }
266}
267
268impl Default for ParamRange {
269 fn default() -> Self {
270 Self::new()
271 }
272}
273
274#[derive(Debug, Clone, PartialEq)]
280pub struct ParamMetadata {
281 pub name: String,
283
284 pub description: String,
286
287 pub typ: ParamType,
289
290 pub default: ParamValue,
292
293 pub range: ParamRange,
295
296 pub unit: Option<String>,
298
299 pub choices: Option<Vec<(String, f32)>>,
301}
302
303impl ParamMetadata {
304 pub fn new(name: impl Into<String>, typ: ParamType, default: ParamValue) -> Self {
306 Self {
307 name: name.into(),
308 description: String::new(),
309 typ,
310 default,
311 range: ParamRange::default(),
312 unit: None,
313 choices: None,
314 }
315 }
316
317 pub fn with_description(mut self, description: impl Into<String>) -> Self {
319 self.description = description.into();
320 self
321 }
322
323 pub fn with_range(mut self, min: f32, max: f32, step: f32) -> Self {
325 self.range = ParamRange::new()
326 .with_min(min)
327 .with_max(max)
328 .with_step(step);
329 self
330 }
331
332 pub fn with_unit(mut self, unit: impl Into<String>) -> Self {
334 self.unit = Some(unit.into());
335 self
336 }
337
338 pub fn with_choices(mut self, choices: Vec<(String, f32)>) -> Self {
340 self.choices = Some(choices);
341 self
342 }
343}
344
345#[cfg(test)]
350mod tests {
351 use super::*;
352
353 #[test]
354 fn test_parameter_id_valid() {
355 assert!(ParameterId::new("gain").is_ok());
356 assert!(ParameterId::new("cutoff_freq").is_ok());
357 assert!(ParameterId::new("delay_time_2").is_ok());
358 }
359
360 #[test]
361 fn test_parameter_id_invalid() {
362 assert!(ParameterId::new("").is_err());
363 assert!(ParameterId::new("1gain").is_err());
364 assert!(ParameterId::new("_gain").is_err());
365
366 let long_name = "a".repeat(ParameterId::MAX_LEN + 1);
367 assert!(ParameterId::new(long_name).is_err());
368 }
369
370 #[test]
371 fn test_param_value_conversion() {
372 let f = ParamValue::Float(42.0);
373 assert_eq!(f.as_f32(), Some(42.0));
374 assert_eq!(f.as_i32(), Some(42));
375 assert_eq!(f.as_bool(), Some(true));
376
377 let i = ParamValue::Int(0);
378 assert_eq!(i.as_f32(), Some(0.0));
379 assert_eq!(i.as_i32(), Some(0));
380 assert_eq!(i.as_bool(), Some(false));
381 }
382}