mint_cli/layout/
block.rs

1use super::entry::LeafEntry;
2use super::errors::LayoutError;
3use super::header::{CrcLocation, Header};
4use super::settings::{Endianness, Settings};
5use crate::variant::DataSheet;
6
7use indexmap::IndexMap;
8use serde::Deserialize;
9
10/// Mutable state tracked during recursive bytestream building
11struct BuildState {
12    buffer: Vec<u8>,
13    offset: usize,
14    padding_count: u32,
15}
16
17/// Immutable configuration for bytestream building
18pub struct BuildConfig<'a> {
19    pub endianness: &'a Endianness,
20    pub padding: u8,
21    pub strict: bool,
22}
23
24#[derive(Debug, Deserialize)]
25pub struct Config {
26    pub settings: Settings,
27    #[serde(flatten)]
28    pub blocks: IndexMap<String, Block>,
29}
30
31/// Flash block.
32#[derive(Debug, Deserialize)]
33pub struct Block {
34    pub header: Header,
35    pub data: Entry,
36}
37
38/// Any entry - should always be either a leaf or a branch (more entries).
39#[derive(Debug, Deserialize)]
40#[serde(untagged)]
41pub enum Entry {
42    Leaf(LeafEntry),
43    Branch(IndexMap<String, Entry>),
44}
45
46impl Block {
47    pub fn build_bytestream(
48        &self,
49        data_sheet: Option<&DataSheet>,
50        settings: &Settings,
51        strict: bool,
52    ) -> Result<(Vec<u8>, u32), LayoutError> {
53        let mut state = BuildState {
54            buffer: Vec::with_capacity((self.header.length as usize).min(64 * 1024)),
55            offset: 0,
56            padding_count: 0,
57        };
58        let config = BuildConfig {
59            endianness: &settings.endianness,
60            padding: self.header.padding,
61            strict,
62        };
63
64        Self::build_bytestream_inner(&self.data, data_sheet, &mut state, &config)?;
65
66        if matches!(self.header.crc_location, CrcLocation::Keyword(_)) {
67            // Padding out to the 4 byte boundary for appended/prepended CRC32
68            while state.offset % 4 != 0 {
69                state.buffer.push(config.padding);
70                state.offset += 1;
71                state.padding_count += 1;
72            }
73        }
74
75        Ok((state.buffer, state.padding_count))
76    }
77
78    fn build_bytestream_inner(
79        table: &Entry,
80        data_sheet: Option<&DataSheet>,
81        state: &mut BuildState,
82        config: &BuildConfig,
83    ) -> Result<(), LayoutError> {
84        match table {
85            Entry::Leaf(leaf) => {
86                let alignment = leaf.get_alignment();
87                while state.offset % alignment != 0 {
88                    state.buffer.push(config.padding);
89                    state.offset += 1;
90                    state.padding_count += 1;
91                }
92
93                let bytes = leaf.emit_bytes(data_sheet, config)?;
94                state.offset += bytes.len();
95                state.buffer.extend(bytes);
96            }
97            Entry::Branch(branch) => {
98                for (field_name, v) in branch.iter() {
99                    Self::build_bytestream_inner(v, data_sheet, state, config).map_err(|e| {
100                        LayoutError::InField {
101                            field: field_name.clone(),
102                            source: Box::new(e),
103                        }
104                    })?;
105                }
106            }
107        }
108        Ok(())
109    }
110}