instrument_core/
registry.rs1use crate::kind::InstrumentKind;
2use serde::Deserialize;
3use std::collections::HashMap;
4use std::path::Path;
5
6#[derive(Debug, Clone, Deserialize)]
8struct RegistryFile {
9 #[serde(default)]
10 entry: Vec<ModelEntry>,
11 #[serde(default)]
12 usb_entry: Vec<UsbEntry>,
13}
14
15#[derive(Debug, Clone, Deserialize)]
16struct ModelEntry {
17 manufacturer: String,
18 model: String,
19 kinds: Vec<String>,
20}
21
22#[derive(Debug, Clone, Deserialize)]
23struct UsbEntry {
24 vid: String,
25 pid: String,
26 manufacturer: Option<String>,
27 model: Option<String>,
28 kinds: Vec<String>,
29}
30
31#[derive(Debug, Default, Clone)]
33pub struct ModelRegistry {
34 by_model: HashMap<String, Vec<InstrumentKind>>,
35 by_usb: HashMap<(String, String), UsbHint>,
36 runtime: HashMap<String, Vec<InstrumentKind>>,
37}
38
39#[derive(Debug, Clone)]
40pub struct UsbHint {
41 pub manufacturer: Option<String>,
42 pub model: Option<String>,
43 pub kinds: Vec<InstrumentKind>,
44}
45
46impl ModelRegistry {
47 pub fn embedded() -> Self {
48 const EMBEDDED: &str = include_str!("../data/model_registry.toml");
49 Self::from_toml(EMBEDDED).unwrap_or_default()
50 }
51
52 pub fn from_toml(content: &str) -> std::result::Result<Self, String> {
53 let file: RegistryFile =
54 toml::from_str(content).map_err(|e| format!("registry parse: {e}"))?;
55 let mut registry = Self::default();
56 for entry in file.entry {
57 let key = normalize_key(&entry.manufacturer, &entry.model);
58 registry.by_model.insert(key, parse_kinds(&entry.kinds));
59 }
60 for entry in file.usb_entry {
61 let key = (entry.vid.to_uppercase(), entry.pid.to_uppercase());
62 registry.by_usb.insert(
63 key,
64 UsbHint {
65 manufacturer: entry.manufacturer,
66 model: entry.model,
67 kinds: parse_kinds(&entry.kinds),
68 },
69 );
70 }
71 Ok(registry)
72 }
73
74 pub fn load_path(path: impl AsRef<Path>) -> Result<Self, String> {
75 let content =
76 std::fs::read_to_string(path.as_ref()).map_err(|e| format!("read registry: {e}"))?;
77 Self::from_toml(&content)
78 }
79
80 pub fn merge(&mut self, other: &Self) {
81 for (k, v) in &other.by_model {
82 self.by_model.entry(k.clone()).or_insert_with(|| v.clone());
83 }
84 for (k, v) in &other.by_usb {
85 self.by_usb.entry(k.clone()).or_insert_with(|| v.clone());
86 }
87 for (k, v) in &other.runtime {
88 self.runtime.insert(k.clone(), v.clone());
89 }
90 }
91
92 pub fn add_runtime(&mut self, manufacturer: &str, model: &str, kinds: Vec<InstrumentKind>) {
93 let key = normalize_key(manufacturer, model);
94 self.runtime.insert(key, kinds);
95 }
96
97 pub fn lookup_model(&self, manufacturer: &str, model: &str) -> Option<Vec<InstrumentKind>> {
98 let key = normalize_key(manufacturer, model);
99 self.runtime
100 .get(&key)
101 .or_else(|| self.by_model.get(&key))
102 .cloned()
103 }
104
105 pub fn lookup_usb(&self, vid: &str, pid: &str) -> Option<UsbHint> {
106 let key = (vid.to_uppercase(), pid.to_uppercase());
107 self.by_usb.get(&key).cloned()
108 }
109}
110
111fn normalize_key(manufacturer: &str, model: &str) -> String {
112 format!(
113 "{}|{}",
114 manufacturer.trim().to_lowercase(),
115 model.trim().to_lowercase()
116 )
117}
118
119fn parse_kinds(labels: &[String]) -> Vec<InstrumentKind> {
120 labels
121 .iter()
122 .filter_map(|l| InstrumentKind::from_str_label(l))
123 .collect()
124}
125
126