diff --git a/src/nvs.rs b/src/nvs.rs index f3656eb6724..8141155631d 100644 --- a/src/nvs.rs +++ b/src/nvs.rs @@ -1,12 +1,12 @@ //! Non-Volatile Storage (NVS) -use core::ptr; +use core::{marker::PhantomData, mem::MaybeUninit, ptr}; extern crate alloc; use alloc::sync::Arc; use ::log::*; -use embedded_svc::storage::{RawStorage, StorageBase}; +use embedded_svc::storage::{RawStorage, StorageBase, StorageEntry, StorageIterate}; use esp_idf_sys::*; @@ -663,6 +663,25 @@ impl EspNvs { Ok(()) } + + pub fn entries<'a>(&'a self) -> Result, EspError> { + let mut iter = MaybeUninit::uninit(); + + match esp!(unsafe { + nvs_entry_find_in_handle(self.1, nvs_type_t_NVS_TYPE_ANY, iter.as_mut_ptr()) + }) { + Err(e) if e.code() == ESP_ERR_NVS_NOT_FOUND => return Ok(EspNvsIterEntries::empty()), + Err(e) => return Err(e), + _ => (), + } + + let iter = unsafe { iter.assume_init() }; + + Ok(EspNvsIterEntries { + nvs: PhantomData, + iter, + }) + } } impl Drop for EspNvs { @@ -710,3 +729,92 @@ impl RawStorage for EspNvs { EspNvs::set_raw(self, name, buf) } } + +impl StorageIterate for EspNvs { + type Error = EspError; + type Entries<'a> = EspNvsIterEntries<'a, T> + where Self: 'a; + type Entry = NvsEntry; + + fn entries<'a>(&'a self) -> Result, Self::Error> { + EspNvs::entries(self) + } +} + +pub struct EspNvsIterEntries<'a, T: NvsPartitionId> { + nvs: PhantomData<&'a EspNvs>, + iter: nvs_iterator_t, +} + +impl<'a, T: NvsPartitionId> EspNvsIterEntries<'a, T> { + fn empty() -> Self { + EspNvsIterEntries { + nvs: PhantomData, + iter: ptr::null_mut(), + } + } +} + +impl<'a, T: NvsPartitionId> Drop for EspNvsIterEntries<'a, T> { + fn drop(&mut self) { + unsafe { + nvs_release_iterator(self.iter); + } + } +} + +impl<'a, T: NvsPartitionId> Iterator for EspNvsIterEntries<'a, T> { + type Item = Result; + + fn next(&mut self) -> Option { + loop { + if self.iter.is_null() { + return None; + } + + let mut info = MaybeUninit::::uninit(); + // NOTE: because args are non-null, no error can occur + unsafe { nvs_entry_info(self.iter, info.as_mut_ptr()) }; + let info = unsafe { info.assume_init() }; + + match esp!(unsafe { nvs_entry_next(&mut self.iter) }) { + Err(err) if err.code() == ESP_ERR_NVS_NOT_FOUND => { + self.iter = ptr::null_mut(); + } + // No other errors are used in the esp-idf for nvs_entry_next when its args are non-null + Err(_err) => unreachable!(), + Ok(_) => (), + } + + // skip non-utf8 keys + // SAFETY: nvs_entry_info_t is garunteed to have a null-terminated key + if unsafe { CStr::from_ptr(info.key.as_ptr()) } + .to_str() + .is_ok() + { + return Some(Ok(NvsEntry { info })); + } + } + } +} + +pub struct NvsEntry { + info: nvs_entry_info_t, +} + +impl NvsEntry { + pub fn name(&self) -> &str { + // SAFETY: nvs_entry_info_t guarantees that the key is null-terminated + // NOTE: should not panic because we check for utf8 in TryFrom + unsafe { CStr::from_ptr(self.info.key.as_ptr()) } + .to_str() + .ok() + .unwrap() + } +} + +impl StorageEntry for NvsEntry { + fn name(&self) -> &str { + NvsEntry::name(self) + } +}