From db1f1ce5ef578f9fa88565a35207ed0eac986e32 Mon Sep 17 00:00:00 2001 From: Cody P Schafer Date: Mon, 3 Apr 2023 22:38:52 -0400 Subject: [PATCH] nvs: support StorageIterate This implements `StorageIterate` for `EspNvs`. Note that while the `Error` on iteration is setup to work, the actual implementation in esp-idf does not return any errors during iteration (other than "Not Found", indicating iteration is complete). As a result, `next()` will always return `Some(Ok(...))` or `None` and never `Some(Err(...))`. --- src/nvs.rs | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 105 insertions(+), 2 deletions(-) diff --git a/src/nvs.rs b/src/nvs.rs index 1e37e33b128..c4e82f20686 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 { @@ -708,3 +727,87 @@ 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 { + 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(_) => (), + } + + Some(Ok(NvsEntry { info })) + } +} + +pub struct NvsEntry { + info: nvs_entry_info_t, +} + +impl NvsEntry { + pub fn name(&self) -> Option<&str> { + self.name_cstr().to_str().ok() + } + + pub fn name_cstr(&self) -> &CStr { + // SAFETY: nvs_entry_info_t guarantees that the key is null-terminated + unsafe { CStr::from_ptr(self.info.key.as_ptr()) } + } +} + +impl StorageEntry for NvsEntry { + fn name(&self) -> Option<&str> { + NvsEntry::name(self).into() + } + + fn name_cstr(&self) -> &CStr { + NvsEntry::name_cstr(self) + } +}