1use crate::Result;
2use crate::config::config_f64;
3use crate::model::{Bounds, PacketBlockLayout, PacketDiagramLayout, PacketWordLayout};
4use crate::text::TextMeasurer;
5use merman_core::diagrams::packet::PacketDiagramRenderModel;
6use serde_json::Value;
7
8pub fn layout_packet_diagram(
9 semantic: &serde_json::Value,
10 diagram_title: Option<&str>,
11 effective_config: &serde_json::Value,
12 _measurer: &dyn TextMeasurer,
13) -> Result<PacketDiagramLayout> {
14 let model: PacketDiagramRenderModel = crate::json::from_value_ref(semantic)?;
15 layout_packet_diagram_typed(&model, diagram_title, effective_config, _measurer)
16}
17
18pub fn layout_packet_diagram_typed(
19 model: &PacketDiagramRenderModel,
20 diagram_title: Option<&str>,
21 effective_config: &serde_json::Value,
22 _measurer: &dyn TextMeasurer,
23) -> Result<PacketDiagramLayout> {
24 let _ = (model.acc_title.as_deref(), model.acc_descr.as_deref());
25
26 fn config_bool(cfg: &Value, path: &[&str]) -> Option<bool> {
27 let mut cur = cfg;
28 for key in path {
29 cur = cur.get(*key)?;
30 }
31 cur.as_bool()
32 }
33
34 fn config_i64(cfg: &Value, path: &[&str]) -> Option<i64> {
35 let mut cur = cfg;
36 for key in path {
37 cur = cur.get(*key)?;
38 }
39 cur.as_i64()
40 }
41
42 let show_bits: bool = config_bool(effective_config, &["packet", "showBits"]).unwrap_or(true);
44 let row_height: f64 = config_f64(effective_config, &["packet", "rowHeight"])
45 .unwrap_or(32.0)
46 .max(1.0);
47 let padding_x: f64 = config_f64(effective_config, &["packet", "paddingX"])
48 .unwrap_or(5.0)
49 .max(0.0);
50 let mut padding_y: f64 = config_f64(effective_config, &["packet", "paddingY"])
53 .unwrap_or(5.0)
54 .max(0.0);
55 if show_bits {
56 padding_y += 10.0;
57 }
58 let bit_width: f64 = config_f64(effective_config, &["packet", "bitWidth"])
59 .unwrap_or(32.0)
60 .max(1.0);
61 let bits_per_row: i64 = config_i64(effective_config, &["packet", "bitsPerRow"])
62 .unwrap_or(32)
63 .max(1);
64
65 let total_row_height = row_height + padding_y;
66 let title_from_semantic = model
67 .title
68 .as_deref()
69 .map(str::trim)
70 .filter(|t| !t.is_empty());
71 let title_from_meta = diagram_title.map(str::trim).filter(|t| !t.is_empty());
72 let has_title = title_from_semantic.or(title_from_meta).is_some();
73
74 let words_count = model.packet.len();
75 let svg_height =
76 total_row_height * ((words_count + 1) as f64) - if has_title { 0.0 } else { row_height };
77 let svg_width = bit_width * (bits_per_row as f64) + 2.0;
78
79 let mut words: Vec<PacketWordLayout> = Vec::new();
80 for (row_number, word) in model.packet.iter().enumerate() {
81 let word_y = (row_number as f64) * total_row_height + padding_y;
82 let mut blocks: Vec<PacketBlockLayout> = Vec::new();
83 for block in word {
84 let block_x = ((block.start % bits_per_row) as f64) * bit_width + 1.0;
85 let width = ((block.end - block.start + 1) as f64) * bit_width - padding_x;
86 blocks.push(PacketBlockLayout {
87 start: block.start,
88 end: block.end,
89 label: block.label.clone(),
90 x: block_x,
91 y: word_y,
92 width,
93 height: row_height,
94 });
95 }
96 words.push(PacketWordLayout { blocks });
97 }
98
99 Ok(PacketDiagramLayout {
100 bounds: Some(Bounds {
101 min_x: 0.0,
102 min_y: 0.0,
103 max_x: svg_width,
104 max_y: svg_height.max(1.0),
105 }),
106 width: svg_width,
107 height: svg_height.max(1.0),
108 row_height,
109 padding_x,
110 padding_y,
111 bit_width,
112 bits_per_row,
113 show_bits,
114 words,
115 })
116}