mint_cli/commands/
mod.rs

1pub mod generate;
2pub mod stats;
3
4use crate::args::Args;
5use crate::error::NvmError;
6use crate::layout;
7use crate::layout::errors::LayoutError;
8use crate::output;
9use crate::output::errors::OutputError;
10use crate::variant::DataSheet;
11use crate::writer::write_output;
12use rayon::prelude::*;
13use stats::{BlockStat, BuildStats};
14use std::time::Instant;
15
16pub fn build_separate_blocks(
17    args: &Args,
18    data_sheet: Option<&DataSheet>,
19) -> Result<BuildStats, NvmError> {
20    let start_time = Instant::now();
21
22    let block_stats: Result<Vec<BlockStat>, NvmError> = args
23        .layout
24        .blocks
25        .par_iter()
26        .map(|input| generate::build_block_single(input, data_sheet, args))
27        .collect();
28
29    let block_stats = block_stats?;
30
31    let mut stats = BuildStats::new();
32    for stat in block_stats {
33        stats.add_block(stat);
34    }
35    stats.total_duration = start_time.elapsed();
36
37    Ok(stats)
38}
39
40pub fn build_single_file(
41    args: &Args,
42    data_sheet: Option<&DataSheet>,
43) -> Result<BuildStats, NvmError> {
44    let start_time = Instant::now();
45
46    let mut ranges = Vec::new();
47    let mut block_ranges: Vec<(String, u32, u32)> = Vec::new();
48    let mut stats = BuildStats::new();
49
50    for input in &args.layout.blocks {
51        let result =
52            (|| {
53                let layout = layout::load_layout(&input.file)?;
54
55                let block = layout
56                    .blocks
57                    .get(&input.name)
58                    .ok_or(LayoutError::BlockNotFound(input.name.clone()))?;
59
60                let (bytestream, padding_bytes) =
61                    block.build_bytestream(data_sheet, &layout.settings, args.layout.strict)?;
62
63                let dr = output::bytestream_to_datarange(
64                    bytestream,
65                    &block.header,
66                    &layout.settings,
67                    layout.settings.byte_swap,
68                    layout.settings.pad_to_end,
69                    padding_bytes,
70                )?;
71
72                let mut crc_bytes = [
73                    dr.crc_bytestream[0],
74                    dr.crc_bytestream[1],
75                    dr.crc_bytestream[2],
76                    dr.crc_bytestream[3],
77                ];
78                if layout.settings.byte_swap {
79                    crc_bytes.swap(0, 1);
80                    crc_bytes.swap(2, 3);
81                }
82                let crc_value = match layout.settings.endianness {
83                    layout::settings::Endianness::Big => u32::from_be_bytes(crc_bytes),
84                    layout::settings::Endianness::Little => u32::from_le_bytes(crc_bytes),
85                };
86
87                let stat = BlockStat {
88                    name: input.name.clone(),
89                    start_address: dr.start_address,
90                    allocated_size: dr.allocated_size,
91                    used_size: dr.used_size,
92                    crc_value,
93                };
94
95                let start = block
96                    .header
97                    .start_address
98                    .checked_add(layout.settings.virtual_offset)
99                    .ok_or(LayoutError::InvalidBlockArgument(
100                        "start_address + virtual_offset overflow".into(),
101                    ))?;
102                let end = start.checked_add(block.header.length).ok_or(
103                    LayoutError::InvalidBlockArgument("start + length overflow".into()),
104                )?;
105
106                Ok((dr, stat, start, end))
107            })()
108            .map_err(|e| NvmError::InBlock {
109                block_name: input.name.clone(),
110                layout_file: input.file.clone(),
111                source: Box::new(e),
112            })?;
113
114        let (dr, stat, start, end) = result;
115        stats.add_block(stat);
116        ranges.push(dr);
117        block_ranges.push((input.name.clone(), start, end));
118    }
119
120    // Detect overlaps between declared block memory ranges (inclusive start, exclusive end)
121    for i in 0..block_ranges.len() {
122        for j in (i + 1)..block_ranges.len() {
123            let (ref name_a, a_start, a_end) = block_ranges[i];
124            let (ref name_b, b_start, b_end) = block_ranges[j];
125
126            let overlap_start = a_start.max(b_start);
127            let overlap_end = a_end.min(b_end);
128
129            if overlap_start < overlap_end {
130                let overlap_size = overlap_end - overlap_start;
131                let msg = format!(
132                    "Block '{}' (0x{:08X}-0x{:08X}) overlaps with block '{}' (0x{:08X}-0x{:08X}). Overlap: 0x{:08X}-0x{:08X} ({} bytes)",
133                    name_a,
134                    a_start,
135                    a_end - 1,
136                    name_b,
137                    b_start,
138                    b_end - 1,
139                    overlap_start,
140                    overlap_end - 1,
141                    overlap_size
142                );
143                return Err(OutputError::BlockOverlapError(msg).into());
144            }
145        }
146    }
147
148    let hex_string = output::emit_hex(
149        &ranges,
150        args.output.record_width as usize,
151        args.output.format,
152    )?;
153
154    write_output(&args.output, "combined", &hex_string)?;
155
156    stats.total_duration = start_time.elapsed();
157
158    Ok(stats)
159}