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
10struct BuildState {
12 buffer: Vec<u8>,
13 offset: usize,
14 padding_count: u32,
15}
16
17pub 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#[derive(Debug, Deserialize)]
33pub struct Block {
34 pub header: Header,
35 pub data: Entry,
36}
37
38#[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 while !state.offset.is_multiple_of(4) {
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.is_multiple_of(alignment) {
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}