From 2f88b50c09ba2bc4992fc65aecbfc2ebd35950f7 Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Mon, 2 Dec 2024 11:48:12 +0000 Subject: [PATCH] Generalize DirKvBlobStore --- src/persist.rs | 124 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 89 insertions(+), 35 deletions(-) diff --git a/src/persist.rs b/src/persist.rs index 9d3c6af..130a0de 100644 --- a/src/persist.rs +++ b/src/persist.rs @@ -258,13 +258,19 @@ pub enum Key { ThreadNetworks = 3, } +impl From for u8 { + fn from(key: Key) -> u8 { + key as u8 + } +} + impl AsRef for Key { fn as_ref(&self) -> &str { match self { Key::Fabrics => "fabrics", - Key::EthNetworks => "eth-networks", - Key::WifiNetworks => "wifi-networks", - Key::ThreadNetworks => "thread-networks", + Key::EthNetworks => "eth-net", + Key::WifiNetworks => "wifi-net", + Key::ThreadNetworks => "thread-net", } } } @@ -331,13 +337,51 @@ where #[cfg(feature = "std")] mod file { + use core::fmt::{self, Display}; + + use std::io::{Read, Write}; + + use log::debug; + use rs_matter::error::Error; use super::{Key, KvBlobStore}; extern crate std; + /// A namespace for all matter-related keys in the directory. + pub struct MatterNamespace; + + impl AsRef for MatterNamespace { + fn as_ref(&self) -> &str { + "matter" + } + } + + /// A helper for logging the key together with its (optional) namespace + struct ForDisplay<'a, N, K>(&'a Option, &'a K); + + impl Display for ForDisplay<'_, N, K> + where + N: AsRef, + K: AsRef, + { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(namespace) = self.0 { + write!(f, "{}::", namespace.as_ref())?; + } + + write!(f, "{}", self.1.as_ref()) + } + } + /// An implementation of the `KvBlobStore` trait that stores the BLOBs in a directory. + /// + /// The BLOBs are stored in files named after the keys (and their namespaces, if any) + /// in the specified directory. + /// + /// The implementation has its own public API, so that the user is able to load and store + /// additional BLOBs, unrelated to the Matter-specific `KvBlobStore`. pub struct DirKvBlobStore(std::path::PathBuf); impl DirKvBlobStore { @@ -353,14 +397,12 @@ mod file { } /// Load a BLOB with the specified key from the directory. - pub fn load(&self, key: Key, buf: &mut [u8], cb: F) -> Result<(), Error> + pub fn load<'a, N, K>(&self, namespace: Option, key: K, buf: &'a mut [u8]) -> Result, Error> where - F: FnOnce(Option<&[u8]>) -> Result<(), Error>, + N: AsRef, + K: AsRef, { - use log::info; - use std::io::Read; - - let path = self.key_path(key); + let path = self.key_path(&namespace, &key); match std::fs::File::open(path) { Ok(mut file) => { @@ -382,55 +424,63 @@ mod file { let data = &buf[..offset]; - info!("Key {}: loaded {} bytes {:?}", key, data.len(), data); + debug!("Key {}: loaded {} bytes {:?}", ForDisplay(&namespace, &key), data.len(), data); - cb(Some(data)) + Ok(Some(data)) } - Err(_) => cb(None), + Err(_) => Ok(None), } } /// Store a BLOB with the specified key in the directory. - fn store(&self, key: Key, buf: &mut [u8], cb: F) -> Result<(), Error> + pub fn store(&self, namespace: Option, key: K, data: &[u8]) -> Result<(), Error> where - F: FnOnce(&mut [u8]) -> Result, + N: AsRef, + K: AsRef, { - use log::info; - use std::io::Write; - - std::fs::create_dir_all(&self.0)?; + let path = self.key_path(&namespace, &key); - let path = self.key_path(key); + std::fs::create_dir_all(path.parent().unwrap())?; let mut file = std::fs::File::create(path)?; - let len = cb(buf)?; - - let data = &buf[..len]; - file.write_all(data)?; - info!("Key {}: stored {} bytes {:?}", key, data.len(), data); + debug!("Key {}: stored {} bytes {:?}", ForDisplay(&namespace, &key), data.len(), data); Ok(()) } /// Remove a BLOB with the specified key from the directory. /// If the BLOB does not exist, this method does nothing. - fn remove(&self, key: Key, _buf: &mut [u8]) -> Result<(), Error> { - use log::info; - - let path = self.key_path(key); + pub fn remove(&self, namespace: Option, key: K) -> Result<(), Error> + where + N: AsRef, + K: AsRef, + { + let path = self.key_path(&namespace, &key); if std::fs::remove_file(path).is_ok() { - info!("Key {}: removed", key); + debug!("Key {}: removed", ForDisplay(&namespace, &key)); } Ok(()) } - fn key_path(&self, key: Key) -> std::path::PathBuf { - self.0.join(key.as_ref()) + fn key_path(&self, namespace: &Option, key: &K) -> std::path::PathBuf + where + N: AsRef, + K: AsRef, + { + let mut path = self.0.clone(); + + if let Some(namespace) = namespace { + path.push(namespace.as_ref()); + } + + path.push(key.as_ref()); + + path } } @@ -445,18 +495,22 @@ mod file { where F: FnOnce(Option<&[u8]>) -> Result<(), Error>, { - DirKvBlobStore::load(self, key, buf, cb) + let data = DirKvBlobStore::load(self, Some(MatterNamespace), key, buf)?; + + cb(data) } async fn store(&mut self, key: Key, buf: &mut [u8], cb: F) -> Result<(), Error> where F: FnOnce(&mut [u8]) -> Result, { - DirKvBlobStore::store(self, key, buf, cb) + let data_len = cb(buf)?; + + DirKvBlobStore::store(self, Some(MatterNamespace), key, &buf[..data_len]) } - async fn remove(&mut self, key: Key, buf: &mut [u8]) -> Result<(), Error> { - DirKvBlobStore::remove(self, key, buf) + async fn remove(&mut self, key: Key, _buf: &mut [u8]) -> Result<(), Error> { + DirKvBlobStore::remove(self, Some(MatterNamespace), key) } } }