Skip to main content

mint_cli/layout/
block.rs

1use super::entry::LeafEntry;
2use super::error::LayoutError;
3use super::header::Header;
4use super::settings::{Endianness, Settings};
5use super::used_values::ValueSink;
6use crate::data::DataSource;
7
8use indexmap::IndexMap;
9use serde::Deserialize;
10
11/// Mutable state tracked during recursive bytestream building
12struct BuildState {
13    buffer: Vec<u8>,
14    offset: usize,
15    padding_count: u32,
16}
17
18/// Immutable configuration for bytestream building
19pub struct BuildConfig<'a> {
20    pub endianness: &'a Endianness,
21    pub padding: u8,
22    pub strict: bool,
23    pub word_addressing: bool,
24}
25
26#[derive(Debug, Deserialize)]
27pub struct Config {
28    pub settings: Settings,
29    #[serde(flatten)]
30    pub blocks: IndexMap<String, Block>,
31}
32
33/// Flash block.
34#[derive(Debug, Deserialize)]
35pub struct Block {
36    pub header: Header,
37    pub data: Entry,
38}
39
40/// Any entry - should always be either a leaf or a branch (more entries).
41#[derive(Debug, Deserialize)]
42#[serde(untagged)]
43pub enum Entry {
44    Leaf(LeafEntry),
45    Branch(IndexMap<String, Entry>),
46}
47
48impl Block {
49    pub fn build_bytestream(
50        &self,
51        data_source: Option<&dyn DataSource>,
52        settings: &Settings,
53        strict: bool,
54        value_sink: &mut dyn ValueSink,
55    ) -> Result<(Vec<u8>, u32), LayoutError> {
56        let mut state = BuildState {
57            buffer: Vec::with_capacity((self.header.length as usize).min(64 * 1024)),
58            offset: 0,
59            padding_count: 0,
60        };
61        let config = BuildConfig {
62            endianness: &settings.endianness,
63            padding: self.header.padding,
64            strict,
65            word_addressing: settings.word_addressing,
66        };
67
68        let mut field_path = Vec::new();
69        Self::build_bytestream_inner(
70            &self.data,
71            data_source,
72            &mut state,
73            &config,
74            value_sink,
75            &mut field_path,
76        )?;
77
78        Ok((state.buffer, state.padding_count))
79    }
80
81    fn build_bytestream_inner(
82        table: &Entry,
83        data_source: Option<&dyn DataSource>,
84        state: &mut BuildState,
85        config: &BuildConfig,
86        value_sink: &mut dyn ValueSink,
87        field_path: &mut Vec<String>,
88    ) -> Result<(), LayoutError> {
89        match table {
90            Entry::Leaf(leaf) => {
91                let alignment = leaf.get_alignment();
92                while !state.offset.is_multiple_of(alignment) {
93                    state.buffer.push(config.padding);
94                    state.offset += 1;
95                    state.padding_count += 1;
96                }
97
98                let bytes = leaf.emit_bytes(data_source, config, value_sink, field_path)?;
99                state.offset += bytes.len();
100                state.buffer.extend(bytes);
101            }
102            Entry::Branch(branch) => {
103                for (field_name, v) in branch.iter() {
104                    let path_len = field_path.len();
105                    let segments = split_field_path(field_name)?;
106                    field_path.extend(segments);
107                    let result = Self::build_bytestream_inner(
108                        v,
109                        data_source,
110                        state,
111                        config,
112                        value_sink,
113                        field_path,
114                    );
115                    field_path.truncate(path_len);
116                    result.map_err(|e| LayoutError::InField {
117                        field: field_name.clone(),
118                        source: Box::new(e),
119                    })?;
120                }
121            }
122        }
123        Ok(())
124    }
125}
126
127fn split_field_path(field_name: &str) -> Result<Vec<String>, LayoutError> {
128    let segments: Vec<&str> = field_name.split('.').collect();
129    if segments.iter().any(|s| s.is_empty()) {
130        return Err(LayoutError::DataValueExportFailed(format!(
131            "Invalid field path '{}'.",
132            field_name
133        )));
134    }
135    Ok(segments.into_iter().map(|s| s.to_string()).collect())
136}