1pub mod block;
2mod conversions;
3mod entry;
4pub mod error;
5pub mod header;
6pub mod scalar_type;
7pub mod settings;
8pub mod used_values;
9pub mod value;
10
11use block::Config;
12use error::LayoutError;
13use std::collections::hash_map::Entry;
14use std::path::Path;
15use toml::Value as TomlValue;
16
17pub fn load_layout(filename: &str) -> Result<Config, LayoutError> {
18 let text = std::fs::read_to_string(filename)
19 .map_err(|_| LayoutError::FileError(format!("failed to open file: {}", filename)))?;
20
21 let ext = Path::new(filename)
22 .extension()
23 .and_then(|s| s.to_str())
24 .map(|s| s.to_ascii_lowercase())
25 .unwrap_or_default();
26
27 match ext.as_str() {
28 "toml" => parse_toml_layout_with_context(&text, &format!("file {}", filename)),
29 "yaml" | "yml" => parse_yaml_layout_with_context(&text, &format!("file {}", filename)),
30 _ => Err(LayoutError::FileError(
31 "Unsupported layout file format; use .toml or .yaml".to_owned(),
32 )),
33 }
34}
35
36pub fn parse_toml_layout(text: &str) -> Result<Config, LayoutError> {
37 parse_toml_layout_with_context(text, "TOML layout")
38}
39
40pub fn parse_yaml_layout(text: &str) -> Result<Config, LayoutError> {
41 parse_yaml_layout_with_context(text, "YAML layout")
42}
43
44fn parse_toml_layout_with_context(text: &str, context: &str) -> Result<Config, LayoutError> {
45 let raw: TomlValue = toml::from_str(text)
46 .map_err(|e| LayoutError::FileError(format!("failed to parse {}: {}", context, e)))?;
47 let mut cfg: Config = raw
48 .try_into()
49 .map_err(|e| LayoutError::FileError(format!("failed to parse {}: {}", context, e)))?;
50 promote_block_header_consts(&mut cfg)?;
51 Ok(cfg)
52}
53
54fn parse_yaml_layout_with_context(text: &str, context: &str) -> Result<Config, LayoutError> {
55 let mut cfg: Config = serde_yaml::from_str(text)
56 .map_err(|e| LayoutError::FileError(format!("failed to parse {}: {}", context, e)))?;
57 promote_block_header_consts(&mut cfg)?;
58 Ok(cfg)
59}
60
61fn promote_block_header_consts(cfg: &mut Config) -> Result<(), LayoutError> {
62 for (block_name, block) in &cfg.blocks {
63 insert_promoted_const(
64 &mut cfg.mint.consts,
65 format!("{block_name}.start_address"),
66 block.header.start_address,
67 )?;
68 insert_promoted_const(
69 &mut cfg.mint.consts,
70 format!("{block_name}.length"),
71 block.header.length,
72 )?;
73 }
74 Ok(())
75}
76
77fn insert_promoted_const(
78 consts: &mut std::collections::HashMap<String, value::ValueSource>,
79 name: String,
80 value: u32,
81) -> Result<(), LayoutError> {
82 match consts.entry(name) {
83 Entry::Occupied(entry) => Err(LayoutError::FileError(format!(
84 "[mint.const] key '{}' collides with auto-promoted block header const",
85 entry.key()
86 ))),
87 Entry::Vacant(entry) => {
88 entry.insert(value::ValueSource::Single(value::DataValue::U64(
89 u64::from(value),
90 )));
91 Ok(())
92 }
93 }
94}