embedded-svc 0.29.0

A set of traits for services higher level than embedded-hal and typically found in embedded microcontrollers with WiFi or BLE support.
Documentation
#[cfg(feature = "use_serde")]
use serde::{Deserialize, Serialize};

use crate::io::{ErrorType, Read, Write};
use crate::utils::io::*;

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "use_serde", derive(Serialize, Deserialize))]
pub struct Slot {
    pub label: heapless::String<32>,
    pub state: SlotState,
    pub firmware: Option<FirmwareInfo>,
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "use_serde", derive(Serialize, Deserialize))]
pub struct FirmwareInfo {
    pub version: heapless::String<24>,
    pub released: heapless::String<24>,
    pub description: Option<heapless::String<128>>,
    pub signature: Option<heapless::Vec<u8, 32>>,
    pub download_id: Option<heapless::String<128>>,
}

#[derive(Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "use_serde", derive(Serialize, Deserialize))]
pub struct UpdateProgress {
    pub progress: u32,
    pub operation: &'static str,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "use_serde", derive(Serialize, Deserialize))]
pub enum LoadResult {
    ReloadMore,
    LoadMore,
    Loaded,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "use_serde", derive(Serialize, Deserialize))]
pub enum SlotState {
    Factory,
    Valid,
    Invalid,
    Unverified,
    Unknown,
}

pub trait FirmwareInfoLoader: ErrorType {
    fn load(&mut self, buf: &[u8]) -> Result<LoadResult, Self::Error>;

    fn is_loaded(&self) -> bool;

    fn get_info(&self) -> Result<FirmwareInfo, Self::Error>;
}

impl<F> FirmwareInfoLoader for &mut F
where
    F: FirmwareInfoLoader,
{
    fn load(&mut self, buf: &[u8]) -> Result<LoadResult, Self::Error> {
        (*self).load(buf)
    }

    fn is_loaded(&self) -> bool {
        (**self).is_loaded()
    }

    fn get_info(&self) -> Result<FirmwareInfo, Self::Error> {
        (**self).get_info()
    }
}

pub trait Ota: ErrorType {
    type Update<'a>: OtaUpdate<Error = Self::Error>
    where
        Self: 'a;

    fn get_boot_slot(&self) -> Result<Slot, Self::Error>;

    fn get_running_slot(&self) -> Result<Slot, Self::Error>;

    fn get_update_slot(&self) -> Result<Slot, Self::Error>;

    fn is_factory_reset_supported(&self) -> Result<bool, Self::Error>;

    fn factory_reset(&mut self) -> Result<(), Self::Error>;

    fn initiate_update(&mut self) -> Result<Self::Update<'_>, Self::Error>;

    fn mark_running_slot_valid(&mut self) -> Result<(), Self::Error>;

    fn mark_running_slot_invalid_and_reboot(&mut self) -> Self::Error;
}

impl<O> Ota for &mut O
where
    O: Ota,
{
    type Update<'a>
        = O::Update<'a>
    where
        Self: 'a;

    fn get_boot_slot(&self) -> Result<Slot, Self::Error> {
        (**self).get_boot_slot()
    }

    fn get_running_slot(&self) -> Result<Slot, Self::Error> {
        (**self).get_running_slot()
    }

    fn get_update_slot(&self) -> Result<Slot, Self::Error> {
        (**self).get_update_slot()
    }

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

    fn factory_reset(&mut self) -> Result<(), Self::Error> {
        (*self).factory_reset()
    }

    fn initiate_update(&mut self) -> Result<Self::Update<'_>, Self::Error> {
        (*self).initiate_update()
    }

    fn mark_running_slot_valid(&mut self) -> Result<(), Self::Error> {
        (*self).mark_running_slot_valid()
    }

    fn mark_running_slot_invalid_and_reboot(&mut self) -> Self::Error {
        (*self).mark_running_slot_invalid_and_reboot()
    }
}

pub trait OtaUpdate: Write {
    type OtaUpdateFinished: OtaUpdateFinished;

    fn finish(self) -> Result<Self::OtaUpdateFinished, Self::Error>;

    fn complete(self) -> Result<(), Self::Error>;

    fn abort(self) -> Result<(), Self::Error>;

    fn update<R>(
        mut self,
        read: R,
        progress: impl Fn(u64, u64),
    ) -> Result<(), CopyError<R::Error, Self::Error>>
    where
        R: Read,
        Self: Sized,
    {
        let mut buf = [0_u8; 64];

        match copy_len_with_progress(read, &mut self, &mut buf, u64::MAX, progress) {
            Ok(_) => self.complete().map_err(CopyError::Write),
            Err(e) => {
                self.abort().map_err(CopyError::Write)?;

                Err(e)
            }
        }
    }
}

pub trait OtaUpdateFinished: ErrorType {
    fn activate(self) -> Result<(), Self::Error>;
}

pub mod asynch {
    use crate::io::asynch::{ErrorType, Read, Write};
    use crate::utils::io::asynch::*;

    pub use super::{FirmwareInfo, FirmwareInfoLoader, LoadResult, Slot, SlotState};

    pub trait Ota: ErrorType {
        type Update<'a>: OtaUpdate<Error = Self::Error>
        where
            Self: 'a;

        async fn get_boot_slot(&self) -> Result<Slot, Self::Error>;

        async fn get_running_slot(&self) -> Result<Slot, Self::Error>;

        async fn get_update_slot(&self) -> Result<Slot, Self::Error>;

        async fn is_factory_reset_supported(&self) -> Result<bool, Self::Error>;

        async fn factory_reset(&mut self) -> Result<(), Self::Error>;

        async fn initiate_update(&mut self) -> Result<Self::Update<'_>, Self::Error>;

        async fn mark_running_slot_valid(&mut self) -> Result<(), Self::Error>;

        async fn mark_running_slot_invalid_and_reboot(&mut self) -> Self::Error;
    }

    impl<O> Ota for &mut O
    where
        O: Ota,
    {
        type Update<'a>
            = O::Update<'a>
        where
            Self: 'a;

        async fn get_boot_slot(&self) -> Result<Slot, Self::Error> {
            (**self).get_boot_slot().await
        }

        async fn get_running_slot(&self) -> Result<Slot, Self::Error> {
            (**self).get_running_slot().await
        }

        async fn get_update_slot(&self) -> Result<Slot, Self::Error> {
            (**self).get_update_slot().await
        }

        async fn is_factory_reset_supported(&self) -> Result<bool, Self::Error> {
            (**self).is_factory_reset_supported().await
        }

        async fn factory_reset(&mut self) -> Result<(), Self::Error> {
            (*self).factory_reset().await
        }

        async fn initiate_update(&mut self) -> Result<Self::Update<'_>, Self::Error> {
            (*self).initiate_update().await
        }

        async fn mark_running_slot_valid(&mut self) -> Result<(), Self::Error> {
            (*self).mark_running_slot_valid().await
        }

        async fn mark_running_slot_invalid_and_reboot(&mut self) -> Self::Error {
            (*self).mark_running_slot_invalid_and_reboot().await
        }
    }

    pub trait OtaUpdate: Write {
        type OtaUpdateFinished: OtaUpdateFinished;

        async fn finish(self) -> Result<Self::OtaUpdateFinished, Self::Error>;

        async fn complete(self) -> Result<(), Self::Error>;

        async fn abort(self) -> Result<(), Self::Error>;

        async fn update<R>(
            self,
            read: R,
            progress: impl Fn(u64, u64),
        ) -> Result<(), CopyError<R::Error, Self::Error>>
        where
            R: Read,
            Self: Sized;
    }

    pub trait OtaUpdateFinished: ErrorType {
        async fn activate(self) -> Result<(), Self::Error>;
    }
}