embedded-svc 0.24.0

A set of traits for services higher level than embedded-hal and typically found in embedded microcontrollers with WiFi or BLE support.
Documentation
use core::any::Any;
use core::fmt::{self, Debug};

use serde::de::DeserializeOwned;
use serde::Serialize;

pub trait StorageBase {
    type Error: Debug;

    fn contains(&self, name: &str) -> Result<bool, Self::Error>;
    fn remove(&mut self, name: &str) -> Result<bool, Self::Error>;
}

impl<S> StorageBase for &mut S
where
    S: StorageBase,
{
    type Error = S::Error;

    fn contains(&self, name: &str) -> Result<bool, Self::Error> {
        (**self).contains(name)
    }

    fn remove(&mut self, name: &str) -> Result<bool, Self::Error> {
        (*self).remove(name)
    }
}

pub trait Storage: StorageBase {
    fn get<T>(&self, name: &str) -> Result<Option<T>, Self::Error>
    where
        T: serde::de::DeserializeOwned;

    fn set<T>(&mut self, name: &str, value: &T) -> Result<bool, Self::Error>
    where
        T: serde::Serialize;
}

impl<S> Storage for &mut S
where
    S: Storage,
{
    fn get<T>(&self, name: &str) -> Result<Option<T>, Self::Error>
    where
        T: serde::de::DeserializeOwned,
    {
        (**self).get(name)
    }

    fn set<T>(&mut self, name: &str, value: &T) -> Result<bool, Self::Error>
    where
        T: serde::Serialize,
    {
        (*self).set(name, value)
    }
}

pub trait DynStorage<'a>: StorageBase {
    fn get(&self, name: &str) -> Result<Option<&'a dyn Any>, Self::Error>;

    fn set(&mut self, name: &'a str, value: &'a dyn Any) -> Result<bool, Self::Error>;
}

impl<'a, D> DynStorage<'a> for &'a mut D
where
    D: DynStorage<'a>,
{
    fn get(&self, name: &str) -> Result<Option<&'a dyn Any>, Self::Error> {
        (**self).get(name)
    }

    fn set(&mut self, name: &'a str, value: &'a dyn Any) -> Result<bool, Self::Error> {
        (*self).set(name, value)
    }
}

pub trait RawStorage: StorageBase {
    fn len(&self, name: &str) -> Result<Option<usize>, Self::Error>;

    fn get_raw<'a>(&self, name: &str, buf: &'a mut [u8]) -> Result<Option<&'a [u8]>, Self::Error>;

    fn set_raw(&mut self, name: &str, buf: &[u8]) -> Result<bool, Self::Error>;
}

impl<R> RawStorage for &mut R
where
    R: RawStorage,
{
    fn len(&self, name: &str) -> Result<Option<usize>, Self::Error> {
        (**self).len(name)
    }

    fn get_raw<'a>(&self, name: &str, buf: &'a mut [u8]) -> Result<Option<&'a [u8]>, Self::Error> {
        (**self).get_raw(name, buf)
    }

    fn set_raw(&mut self, name: &str, buf: &[u8]) -> Result<bool, Self::Error> {
        (**self).set_raw(name, buf)
    }
}

pub trait SerDe {
    type Error: Debug;

    fn serialize<'a, T>(&self, slice: &'a mut [u8], value: &T) -> Result<&'a [u8], Self::Error>
    where
        T: Serialize;

    fn deserialize<T>(&self, slice: &[u8]) -> Result<T, Self::Error>
    where
        T: DeserializeOwned;
}

impl<S> SerDe for &S
where
    S: SerDe,
{
    type Error = S::Error;

    fn serialize<'a, T>(&self, slice: &'a mut [u8], value: &T) -> Result<&'a [u8], Self::Error>
    where
        T: Serialize,
    {
        (*self).serialize(slice, value)
    }

    fn deserialize<T>(&self, slice: &[u8]) -> Result<T, Self::Error>
    where
        T: DeserializeOwned,
    {
        (*self).deserialize(slice)
    }
}

#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum StorageError<R, S> {
    RawStorageError(R),
    SerdeError(S),
}

impl<R, S> fmt::Display for StorageError<R, S>
where
    R: fmt::Display,
    S: fmt::Display,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::RawStorageError(e) => write!(f, "Storage error: {e}"),
            Self::SerdeError(e) => write!(f, "SerDe error: {e}"),
        }
    }
}

#[cfg(feature = "std")]
impl<R, S> std::error::Error for StorageError<R, S>
where
    R: std::error::Error,
    S: std::error::Error,
{
}

pub struct StorageImpl<const N: usize, R, S> {
    raw_storage: R,
    serde: S,
}

impl<const N: usize, R, S> StorageImpl<N, R, S>
where
    R: RawStorage,
    S: SerDe,
{
    pub const fn new(raw_storage: R, serde: S) -> Self {
        Self { raw_storage, serde }
    }

    pub fn contains(&self, name: &str) -> Result<bool, StorageError<R::Error, S::Error>> {
        self.raw_storage
            .contains(name)
            .map_err(StorageError::RawStorageError)
    }

    pub fn remove(&mut self, name: &str) -> Result<bool, StorageError<R::Error, S::Error>> {
        self.raw_storage
            .remove(name)
            .map_err(StorageError::RawStorageError)
    }

    pub fn get<T>(&self, name: &str) -> Result<Option<T>, StorageError<R::Error, S::Error>>
    where
        T: DeserializeOwned,
    {
        let mut buf = [0_u8; N];

        if let Some(buf) = self
            .raw_storage
            .get_raw(name, &mut buf)
            .map_err(StorageError::RawStorageError)?
        {
            Ok(Some(
                self.serde
                    .deserialize(buf)
                    .map_err(StorageError::SerdeError)?,
            ))
        } else {
            Ok(None)
        }
    }

    pub fn set<T>(
        &mut self,
        name: &str,
        value: &T,
    ) -> Result<bool, StorageError<R::Error, S::Error>>
    where
        T: Serialize,
    {
        let mut buf = [0_u8; N];

        let buf = self
            .serde
            .serialize(&mut buf, value)
            .map_err(StorageError::SerdeError)?;

        self.raw_storage
            .set_raw(name, buf)
            .map_err(StorageError::RawStorageError)
    }
}

impl<const N: usize, R, S> StorageBase for StorageImpl<N, R, S>
where
    R: RawStorage,
    S: SerDe,
{
    type Error = StorageError<R::Error, S::Error>;

    fn contains(&self, name: &str) -> Result<bool, Self::Error> {
        StorageImpl::contains(self, name)
    }

    fn remove(&mut self, name: &str) -> Result<bool, Self::Error> {
        StorageImpl::remove(self, name)
    }
}

impl<const N: usize, R, S> Storage for StorageImpl<N, R, S>
where
    R: RawStorage,
    S: SerDe,
{
    fn get<T>(&self, name: &str) -> Result<Option<T>, Self::Error>
    where
        T: DeserializeOwned,
    {
        StorageImpl::get(self, name)
    }

    fn set<T>(&mut self, name: &str, value: &T) -> Result<bool, Self::Error>
    where
        T: Serialize,
    {
        StorageImpl::set(self, name, value)
    }
}

struct Entry<'a> {
    name: &'a str,
    value: &'a dyn Any,
}

#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct NoSpaceError;

pub struct DynStorageImpl<'a, const N: usize>([Option<Entry<'a>>; N]);

impl<'a, const N: usize> DynStorageImpl<'a, N> {
    pub fn contains(&self, name: &str) -> Result<bool, NoSpaceError> {
        Ok(self.get(name)?.is_some())
    }

    pub fn remove(&mut self, name: &str) -> Result<bool, NoSpaceError> {
        if let Some(place) = self.0.iter_mut().find(|entry| {
            entry
                .as_ref()
                .map(|entry| entry.name == name)
                .unwrap_or(false)
        }) {
            *place = None;
            Ok(true)
        } else {
            Ok(false)
        }
    }

    #[allow(clippy::unnecessary_lazy_evaluations)]
    pub fn get(&self, name: &str) -> Result<Option<&'a dyn Any>, NoSpaceError> {
        Ok(self.0.iter().find_map(|entry| {
            entry
                .as_ref()
                .and_then(|entry| (entry.name == name).then(|| entry.value))
        }))
    }

    pub fn set(&mut self, name: &'a str, value: &'a dyn Any) -> Result<bool, NoSpaceError> {
        if let Some(entry) = self
            .0
            .iter_mut()
            .find(|entry| {
                entry
                    .as_ref()
                    .map(|entry| entry.name == name)
                    .unwrap_or(false)
            })
            .and_then(|entry| entry.as_mut())
        {
            entry.value = value;
            Ok(true)
        } else if let Some(place) = self.0.iter_mut().find(|entry| entry.is_none()) {
            *place = Some(Entry { name, value });
            Ok(false)
        } else {
            Err(NoSpaceError)
        }
    }
}

impl<'a, const N: usize> StorageBase for DynStorageImpl<'a, N> {
    type Error = NoSpaceError;

    fn contains(&self, name: &str) -> Result<bool, Self::Error> {
        DynStorageImpl::contains(self, name)
    }

    fn remove(&mut self, name: &str) -> Result<bool, Self::Error> {
        DynStorageImpl::remove(self, name)
    }
}

impl<'a, const N: usize> DynStorage<'a> for DynStorageImpl<'a, N> {
    #[allow(clippy::unnecessary_lazy_evaluations)]
    fn get(&self, name: &str) -> Result<Option<&'a dyn Any>, Self::Error> {
        DynStorageImpl::get(self, name)
    }

    fn set(&mut self, name: &'a str, value: &'a dyn Any) -> Result<bool, Self::Error> {
        DynStorageImpl::set(self, name, value)
    }
}