1use std::any::Any;
10use std::error::Error;
11use std::fmt::{self, Write as _};
12
13use crate::preset::template::TemplateSource;
14use crate::synth::{Synth, SynthError};
15
16pub trait PresetFormat: Send + Sync {
18 fn synth(&self) -> Synth;
20
21 fn extension(&self) -> &'static str;
23
24 fn max_bytes(&self) -> usize;
26
27 fn read(&self, bytes: &[u8]) -> Result<Box<dyn PresetModel>, FormatError>;
33
34 fn read_header(&self, bytes: &[u8]) -> Result<Header, FormatError>;
40
41 fn write(&self, model: &dyn PresetModel) -> Result<Vec<u8>, FormatError>;
49
50 fn absent_means_default(&self, key: &str) -> bool;
52
53 fn template(&self) -> &dyn TemplateSource;
55
56 fn route_sources(&self) -> &'static [&'static str];
58
59 fn default_polarity(&self, source: &str) -> RoutePolarity;
61
62 fn check_value(&self, key: &str, stored: f64) -> Result<(), FormatError>;
69
70 fn scan(&self, model: &dyn PresetModel, edited: &[&str]) -> Result<Vec<String>, FormatError>;
78
79 fn factory_folder(&self) -> &'static [&'static str];
83}
84
85pub trait PresetModel: fmt::Debug + Send + Sync {
87 fn header(&self) -> Header;
89
90 fn get(&self, key: &str) -> Option<Stored>;
93
94 fn set(&mut self, key: &str, value: Stored) -> Result<(), FormatError>;
101
102 fn reset(&mut self, key: &str);
104
105 fn routes(&self) -> Vec<Option<Route>>;
107
108 fn set_route(&mut self, slot: usize, route: Option<Route>) -> Result<(), FormatError>;
115
116 fn set_name(&mut self, name: &str) -> Result<(), FormatError>;
122
123 fn set_author(&mut self, author: &str) -> Result<(), FormatError>;
129
130 fn set_description(&mut self, description: &str) -> Result<(), FormatError>;
136
137 fn set_tags(&mut self, tags: &[String]) -> Result<(), FormatError>;
143
144 fn describe(&self, opts: &DescribeOptions) -> Description;
146
147 fn unknown_keys(&self) -> Vec<String>;
150
151 fn as_any(&self) -> &dyn Any;
153}
154
155#[derive(Clone, Debug, PartialEq)]
157pub enum Stored {
158 Float(f64),
160 Int(i64),
162 Flag(bool),
164 Text(String),
166}
167
168impl fmt::Display for Stored {
169 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170 match self {
171 Self::Float(value) => write!(f, "{value:?}"),
172 Self::Int(value) => write!(f, "{value}"),
173 Self::Flag(value) => write!(f, "{value}"),
174 Self::Text(value) => write!(f, "{value:?}"),
175 }
176 }
177}
178
179#[derive(Clone, Copy, Debug, PartialEq, Eq)]
181pub enum RoutePolarity {
182 Unipolar,
184 Bipolar,
186}
187
188#[derive(Clone, Debug, PartialEq)]
190pub struct Route {
191 pub source: String,
193 pub destination: String,
195 pub amount: f64,
197 pub polarity: RoutePolarity,
199 pub extras: Vec<(String, Stored)>,
201}
202
203#[derive(Clone, Copy, Debug, PartialEq, Eq)]
205pub enum Status {
206 Ok,
208 Warn,
210 Bad,
212}
213
214#[derive(Clone, Debug, PartialEq, Eq)]
216pub struct HeaderField {
217 pub label: String,
219 pub value: String,
221 pub status: Option<Status>,
223}
224
225#[derive(Clone, Debug, Default, PartialEq, Eq)]
228pub struct Header {
229 pub fields: Vec<HeaderField>,
231}
232
233impl Header {
234 pub fn push(&mut self, label: &str, value: impl Into<String>, status: Option<Status>) {
236 self.fields.push(HeaderField {
237 label: label.to_owned(),
238 value: value.into(),
239 status,
240 });
241 }
242}
243
244impl fmt::Display for Header {
245 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
246 let width = self
247 .fields
248 .iter()
249 .map(|field| field.label.len())
250 .max()
251 .unwrap_or(0);
252 for field in &self.fields {
253 write!(f, "{:width$} {}", field.label, field.value)?;
254 match field.status {
255 Some(Status::Warn) => write!(f, " [warn]")?,
256 Some(Status::Bad) => write!(f, " [bad]")?,
257 Some(Status::Ok) | None => {}
258 }
259 writeln!(f)?;
260 }
261 Ok(())
262 }
263}
264
265#[derive(Clone, Copy, Debug, PartialEq, Eq)]
267pub struct DescribeOptions {
268 pub full: bool,
270 pub shown: usize,
272}
273
274impl Default for DescribeOptions {
275 fn default() -> Self {
276 Self {
277 full: false,
278 shown: 3,
279 }
280 }
281}
282
283pub const SHOWN_TEXT_CHARS: usize = 64;
285
286#[derive(Clone, Debug, PartialEq)]
288pub enum Node {
289 Map(Vec<(String, Node)>),
291 List {
293 len: usize,
295 items: Vec<Node>,
297 },
298 Scalar {
300 text: String,
302 mark: Option<String>,
304 },
305}
306
307impl Node {
308 #[must_use]
310 pub fn scalar(text: impl Into<String>) -> Self {
311 Self::Scalar {
312 text: text.into(),
313 mark: None,
314 }
315 }
316}
317
318#[derive(Clone, Debug, PartialEq)]
320pub struct Description {
321 pub root: Node,
323}
324
325impl fmt::Display for Description {
326 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
327 let mut out = String::new();
328 render(&self.root, 0, &mut out)?;
329 f.write_str(&out)
330 }
331}
332
333fn render(node: &Node, depth: usize, out: &mut String) -> fmt::Result {
334 let indent = " ".repeat(depth);
335 match node {
336 Node::Map(children) => {
337 for (key, child) in children {
338 write!(out, "{indent}{key}:")?;
339 render_value(child, depth, out)?;
340 }
341 }
342 Node::List { items, len } => {
343 for item in items {
344 write!(out, "{indent}-")?;
345 render_value(item, depth, out)?;
346 }
347 if *len > items.len() {
348 writeln!(out, "{indent}- ... {} more", len - items.len())?;
349 }
350 }
351 Node::Scalar { text, mark } => {
352 write!(out, "{indent}{text}")?;
353 if let Some(mark) = mark {
354 write!(out, " ({mark})")?;
355 }
356 writeln!(out)?;
357 }
358 }
359 Ok(())
360}
361
362fn render_value(node: &Node, depth: usize, out: &mut String) -> fmt::Result {
364 match node {
365 Node::Scalar { text, mark } => {
366 write!(out, " {text}")?;
367 if let Some(mark) = mark {
368 write!(out, " ({mark})")?;
369 }
370 writeln!(out)
371 }
372 Node::Map(children) if children.is_empty() => writeln!(out, " {{}}"),
373 Node::Map(_) => {
374 writeln!(out)?;
375 render(node, depth + 1, out)
376 }
377 Node::List { len, .. } => {
378 writeln!(out, " list of {len}")?;
379 render(node, depth + 1, out)
380 }
381 }
382}
383
384#[derive(Clone, Debug, PartialEq, Eq)]
388pub struct FormatError {
389 pub what: String,
391 pub cause: String,
393 pub fix: String,
395}
396
397impl FormatError {
398 pub fn new(what: impl Into<String>, cause: impl Into<String>, fix: impl Into<String>) -> Self {
400 Self {
401 what: what.into(),
402 cause: cause.into(),
403 fix: fix.into(),
404 }
405 }
406}
407
408impl fmt::Display for FormatError {
409 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
410 write!(f, "{}: {}. {}", self.what, self.cause, self.fix)
411 }
412}
413
414impl Error for FormatError {}
415
416pub fn format_for(synth: Synth) -> Result<&'static dyn PresetFormat, SynthError> {
422 match synth.ready()? {
423 Synth::Vital => Ok(&crate::preset::vital::FORMAT),
424 Synth::Serum2 => Err(SynthError::NotBuilt(synth)),
425 }
426}
427
428#[cfg(test)]
429mod tests {
430 use super::*;
431
432 #[test]
433 fn describe_renders_an_indented_tree() {
434 let tree = Description {
435 root: Node::Map(vec![
436 ("name".into(), Node::scalar("\"Init\"")),
437 (
438 "settings".into(),
439 Node::Map(vec![
440 (
441 "cutoff".into(),
442 Node::Scalar {
443 text: "200.0".into(),
444 mark: Some("out of range: 8.0 to 136.0".into()),
445 },
446 ),
447 ("empty".into(), Node::Map(Vec::new())),
448 ]),
449 ),
450 (
451 "slots".into(),
452 Node::List {
453 len: 5,
454 items: vec![
455 Node::Map(vec![("source".into(), Node::scalar("\"lfo_1\""))]),
456 Node::scalar("2"),
457 ],
458 },
459 ),
460 ]),
461 };
462 assert_eq!(
463 tree.to_string(),
464 "name: \"Init\"\n\
465 settings:\n cutoff: 200.0 (out of range: 8.0 to 136.0)\n empty: {}\n\
466 slots: list of 5\n -\n source: \"lfo_1\"\n - 2\n - ... 3 more\n"
467 );
468 }
469
470 #[test]
471 fn header_aligns_labels_and_marks_status() {
472 let mut header = Header::default();
473 header.push("build", "1.0.6", Some(Status::Ok));
474 header.push("unknown keys", "2", Some(Status::Warn));
475 assert_eq!(
476 header.to_string(),
477 "build 1.0.6\nunknown keys 2 [warn]\n"
478 );
479 }
480
481 #[test]
482 fn errors_print_three_parts() {
483 let error = FormatError::new("cannot read x", "no `settings`", "Save it again");
484 assert_eq!(
485 error.to_string(),
486 "cannot read x: no `settings`. Save it again"
487 );
488 }
489
490 #[test]
491 fn serum2_has_no_format_yet() {
492 assert_eq!(
493 format_for(Synth::Serum2).err(),
494 Some(SynthError::NotBuilt(Synth::Serum2))
495 );
496 }
497}