diff --git a/src/dbus.rs b/src/dbus.rs index 06353c1..08ac8bb 100644 --- a/src/dbus.rs +++ b/src/dbus.rs @@ -331,6 +331,90 @@ impl Dbus { } }, ); + + b.method_with_cr_async( + "SetInputName", + ("serial_number", "input", "name"), + (), + |mut ctx, cr, (serial_number, key, name): (String, u8, String)| { + log::debug!("SetInputName"); + let state: &mut DbusState = cr.data_mut(ctx.path()).unwrap(); + + let store = state.store.clone(); + + async move { + if store + .set_input_name(&serial_number, key, &name) + .await + .is_ok() + { + return ctx.reply(Ok(())); + } + + ctx.reply(Err(MethodErr::failed("Failed to set input name"))) + } + }, + ); + + b.method_with_cr_async( + "GetInputNames", + ("serial_number",), + ("names",), + |mut ctx, cr, (serial_number,): (String,)| { + log::debug!("InputNames"); + let state: &mut DbusState = cr.data_mut(ctx.path()).unwrap(); + + let store = state.store.clone(); + + async move { + if let Ok(names) = store.input_names(&serial_number).await { + return ctx.reply(Ok((names.into_iter().collect::>(),))); + } + + ctx.reply(Err(MethodErr::failed("Failed to get input names"))) + } + }, + ); + + b.method_with_cr_async( + "SetDeckName", + ("serial_number", "name"), + (), + |mut ctx, cr, (serial_number, name): (String, String)| { + log::debug!("SetDeckName"); + let state: &mut DbusState = cr.data_mut(ctx.path()).unwrap(); + + let store = state.store.clone(); + + async move { + if store.set_deck_name(&serial_number, &name).await.is_ok() { + return ctx.reply(Ok(())); + } + + ctx.reply(Err(MethodErr::failed("Failed to set deck name"))) + } + }, + ); + + b.method_with_cr_async( + "GetDeckName", + ("serial_number",), + ("name",), + |mut ctx, cr, (serial_number,): (String,)| { + log::debug!("GetDeckName"); + let state: &mut DbusState = cr.data_mut(ctx.path()).unwrap(); + + let store = state.store.clone(); + + async move { + if let Ok(Some(name)) = store.deck_name(&serial_number).await { + return ctx.reply(Ok((name,))); + } + + ctx.reply(Err(MethodErr::failed("Failed to fetch deck name"))) + } + }, + ); }); cr.insert("/dog/asonix/git/asonix/Streamdeck", &[iface_token], state); diff --git a/src/store.rs b/src/store.rs index d363f39..c3e0f21 100644 --- a/src/store.rs +++ b/src/store.rs @@ -1,10 +1,12 @@ use crate::message::Command; use sled::{Db, IVec, Tree}; +use std::collections::HashMap; #[derive(Clone, Debug)] pub(crate) struct Store { commands: Tree, settings: Tree, + names: Tree, db: Db, } @@ -14,12 +16,79 @@ impl Store { Ok(Store { commands: db.open_tree("dog.asonix.git.asonix.streamdeck/commands")?, settings: db.open_tree("dog.asonix.git.asonix.streamdeck/settings")?, + names: db.open_tree("dog.asonix.git.asonix.streamdeck/names")?, db, }) as Result }) .await? } + pub(crate) async fn set_input_name( + &self, + deck: &str, + input: u8, + name: &str, + ) -> Result<(), anyhow::Error> { + let key = self.input_name_key(deck, input); + let value = name.as_bytes().to_vec(); + + let names = self.names.clone(); + tokio::task::spawn_blocking(move || names.insert(key, value)).await??; + Ok(()) + } + + pub(crate) async fn input_names( + &self, + deck: &str, + ) -> Result, anyhow::Error> { + let prefix = self.input_name_prefix(deck); + let prefix_len = prefix.len(); + + let names = self.names.clone(); + let vec = tokio::task::spawn_blocking(move || { + names + .scan_prefix(prefix.clone()) + .filter_map(|res| { + let (key, value) = res.ok()?; + + if key.len() != prefix_len + 1 { + return None; + } + + let input = key[prefix_len]; + + let value = String::from_utf8_lossy(&value).to_string(); + Some((input, value)) + }) + .collect() + }) + .await?; + + Ok(vec) + } + + pub(crate) async fn set_deck_name(&self, deck: &str, name: &str) -> Result<(), anyhow::Error> { + let key = self.deck_name_key(deck); + let value = name.as_bytes().to_vec(); + + let names = self.names.clone(); + tokio::task::spawn_blocking(move || names.insert(key, value)).await??; + Ok(()) + } + + pub(crate) async fn deck_name(&self, deck: &str) -> Result, anyhow::Error> { + let key = self.deck_name_key(deck); + + let names = self.names.clone(); + let opt = tokio::task::spawn_blocking(move || names.get(key)).await??; + + if let Some(ivec) = opt { + return Ok(Some(String::from_utf8_lossy(&ivec).to_string())); + } + + Ok(None) + } + pub(crate) async fn setting(&self, key: &str) -> Result, anyhow::Error> where T: serde::de::DeserializeOwned, @@ -119,6 +188,30 @@ impl Store { Ok(vec) } + fn deck_name_key(&self, deck: &str) -> IVec { + let mut key = b"names/".to_vec(); + key.extend(deck.as_bytes()); + + IVec::from(key) + } + + fn input_name_prefix(&self, deck: &str) -> IVec { + let mut key = b"names/".to_vec(); + key.extend(deck.as_bytes()); + key.push(b'/'); + + IVec::from(key) + } + + fn input_name_key(&self, deck: &str, input: u8) -> IVec { + let mut key = b"names/".to_vec(); + key.extend(deck.as_bytes()); + key.push(b'/'); + key.push(input); + + IVec::from(key) + } + fn cmd_prefix(&self, deck: &str) -> IVec { let mut prefix = deck.as_bytes().to_vec(); prefix.push(b'/');