sim_lib_plugin_lv2/
state.rs1use sim_kernel::{Error, Expr, Result, Symbol};
2use sim_lib_plugin_core::PluginState;
3use sim_value::kind::expr_kind;
4
5const LIB_NS: &str = "plugin-lv2";
6
7#[derive(Clone, Debug, PartialEq)]
13pub struct Lv2StatePatch {
14 plugin_uri: String,
15 state: PluginState,
16}
17
18impl Lv2StatePatch {
19 pub fn new(plugin_uri: impl Into<String>, state: PluginState) -> Result<Self> {
25 let plugin_uri = plugin_uri.into();
26 if plugin_uri.trim().is_empty() {
27 return Err(Error::Eval(
28 "LV2 state patch plugin URI cannot be empty".to_owned(),
29 ));
30 }
31 Ok(Self { plugin_uri, state })
32 }
33
34 pub fn plugin_uri(&self) -> &str {
36 &self.plugin_uri
37 }
38
39 pub fn state(&self) -> &PluginState {
41 &self.state
42 }
43
44 pub fn to_expr(&self) -> Expr {
46 Expr::Map(vec![
47 (
48 field("tag"),
49 Expr::Symbol(Symbol::qualified(LIB_NS, "state-patch")),
50 ),
51 (field("uri"), Expr::String(self.plugin_uri.clone())),
52 (field("state"), self.state.to_expr()),
53 ])
54 }
55
56 pub fn from_expr(expr: &Expr) -> Result<Self> {
63 let map = expr_map(expr, "LV2 state patch")?;
64 match lookup(map, "tag") {
65 Some(Expr::Symbol(symbol)) if is_symbol(symbol, LIB_NS, "state-patch") => {}
66 Some(_) => return Err(Error::Eval("LV2 state patch tag is invalid".to_owned())),
67 None => return Err(missing("tag")),
68 }
69 Self::new(
70 expr_string(lookup_required(map, "uri")?, "LV2 plugin URI")?.to_owned(),
71 PluginState::from_expr(lookup_required(map, "state")?)?,
72 )
73 }
74}
75
76fn field(name: &'static str) -> Expr {
77 sim_value::build::qsym(LIB_NS, name)
78}
79
80fn expr_map<'a>(expr: &'a Expr, context: &str) -> Result<&'a [(Expr, Expr)]> {
81 match expr {
82 Expr::Map(entries) => Ok(entries),
83 other => Err(Error::Eval(format!(
84 "expected {context} map, found {}",
85 expr_kind(other)
86 ))),
87 }
88}
89
90fn expr_string<'a>(expr: &'a Expr, context: &str) -> Result<&'a str> {
91 match expr {
92 Expr::String(text) => Ok(text),
93 other => Err(Error::Eval(format!(
94 "expected {context} string, found {}",
95 expr_kind(other)
96 ))),
97 }
98}
99
100fn lookup_required<'a>(map: &'a [(Expr, Expr)], name: &str) -> Result<&'a Expr> {
101 lookup(map, name).ok_or_else(|| missing(name))
102}
103
104fn lookup<'a>(map: &'a [(Expr, Expr)], name: &str) -> Option<&'a Expr> {
105 map.iter().find_map(|(key, value)| match key {
106 Expr::Symbol(symbol) if is_symbol(symbol, LIB_NS, name) => Some(value),
107 _ => None,
108 })
109}
110
111fn is_symbol(symbol: &Symbol, namespace: &str, name: &str) -> bool {
112 symbol.namespace.as_deref() == Some(namespace) && symbol.name.as_ref() == name
113}
114
115fn missing(field: &str) -> Error {
116 Error::Eval(format!("LV2 state patch field is missing: {field}"))
117}