esp_partition_table/
estor.rs

1use crate::{
2    PartitionBuffer, PartitionEntry, PartitionError, PartitionReaderState, PartitionTable,
3    PartitionWriterState,
4};
5use core::{mem::MaybeUninit, ops::Deref};
6use embedded_storage::{ReadStorage, Storage};
7
8/// Error type for embedded storage operations
9#[derive(Clone, Copy, Debug, PartialEq, Eq)]
10pub enum StorageOpError<S: ReadStorage> {
11    /// Partition specific error
12    PartitionError(PartitionError),
13    /// Storage specific error
14    StorageError(S::Error),
15}
16
17impl<S: ReadStorage> From<PartitionError> for StorageOpError<S> {
18    fn from(error: PartitionError) -> Self {
19        Self::PartitionError(error)
20    }
21}
22
23impl PartitionTable {
24    /// Get iterator over partitions from table
25    ///
26    /// If `md5` feature isn't enabled `calc_md5` argument will be ignored.
27    pub fn iter_storage<'s, S>(
28        &self,
29        storage: &'s mut S,
30        calc_md5: bool,
31    ) -> PartitionStorageIter<'s, S>
32    where
33        S: ReadStorage,
34    {
35        PartitionStorageIter {
36            storage,
37            state: PartitionReaderState::new(self.addr, self.size, calc_md5),
38            buffer: MaybeUninit::uninit(),
39        }
40    }
41
42    /// Read partitions from table
43    ///
44    /// The `check_md5` argument means following:
45    /// - None - ignore MD5 checksum
46    /// - Some(false) - check MD5 when found (optional MD5)
47    /// - Some(true) - MD5 checksum is mandatory
48    ///
49    /// If `md5` feature isn't enabled `check_md5` argument will be ignored.
50    #[cfg(feature = "embedded-storage")]
51    pub fn read_storage<S, T>(
52        &self,
53        storage: &mut S,
54        check_md5: Option<bool>,
55    ) -> Result<T, StorageOpError<S>>
56    where
57        S: ReadStorage,
58        T: FromIterator<PartitionEntry>,
59    {
60        let mut iter = self.iter_storage(storage, check_md5.is_some());
61        let result = (&mut iter).collect::<Result<_, _>>()?;
62
63        #[cfg(feature = "md5")]
64        if let Some(mandatory_md5) = check_md5 {
65            if !iter.check_md5().unwrap_or(!mandatory_md5) {
66                return Err(PartitionError::InvalidMd5.into());
67            }
68        }
69
70        Ok(result)
71    }
72
73    /// Write partitions into table
74    ///
75    /// If `md5` feature isn't enabled `write_md5` argument will be ignored.
76    #[cfg(feature = "embedded-storage")]
77    pub fn write_storage<S>(
78        &self,
79        storage: &mut S,
80        partitions: impl IntoIterator<Item = impl AsRef<PartitionEntry>>,
81        write_md5: bool,
82    ) -> Result<usize, StorageOpError<S>>
83    where
84        S: Storage,
85    {
86        let mut data = MaybeUninit::<PartitionBuffer>::uninit();
87        let mut state = PartitionWriterState::new(self.addr, self.size, write_md5);
88
89        for partition in partitions {
90            if state.is_done() {
91                return Err(PartitionError::TooManyData.into());
92            }
93
94            state.write(unsafe { data.assume_init_mut() }, partition)?;
95
96            storage
97                .write(state.offset(), unsafe { data.assume_init_ref() })
98                .map_err(StorageOpError::StorageError)?;
99        }
100
101        #[cfg(feature = "md5")]
102        if write_md5 {
103            if state.is_done() {
104                return Err(PartitionError::TooManyData.into());
105            }
106
107            state.write_md5(unsafe { data.assume_init_mut() })?;
108
109            storage
110                .write(state.offset(), unsafe { data.assume_init_ref() })
111                .map_err(StorageOpError::StorageError)?;
112        }
113
114        Ok((state.offset() - self.addr) as usize)
115    }
116}
117
118/// Iterator over embedded partition table
119pub struct PartitionStorageIter<'s, S> {
120    storage: &'s mut S,
121    state: PartitionReaderState,
122    buffer: MaybeUninit<PartitionBuffer>,
123}
124
125impl<S> PartitionStorageIter<'_, S> {
126    /// Read next partition entry
127    pub fn next_partition(&mut self) -> Result<PartitionEntry, StorageOpError<S>>
128    where
129        S: ReadStorage,
130    {
131        if self.state.is_done() {
132            return Err(StorageOpError::PartitionError(
133                PartitionError::NotEnoughData,
134            ));
135        }
136
137        if let Err(error) = self.storage.read(self.state.offset(), unsafe {
138            self.buffer.assume_init_mut()
139        }) {
140            return Err(StorageOpError::StorageError(error));
141        }
142
143        self.state
144            .read(unsafe { self.buffer.assume_init_ref() })
145            .map_err(From::from)
146    }
147}
148
149impl<S> Deref for PartitionStorageIter<'_, S> {
150    type Target = PartitionReaderState;
151
152    fn deref(&self) -> &Self::Target {
153        &self.state
154    }
155}
156
157impl<S> Iterator for PartitionStorageIter<'_, S>
158where
159    S: ReadStorage,
160{
161    type Item = Result<PartitionEntry, StorageOpError<S>>;
162
163    fn next(&mut self) -> Option<Self::Item> {
164        self.next_partition()
165            .map(Some)
166            .or_else(|error| {
167                if matches!(
168                    error,
169                    StorageOpError::PartitionError(PartitionError::NotEnoughData)
170                ) {
171                    Ok(None)
172                } else {
173                    Err(error)
174                }
175            })
176            .transpose()
177    }
178}