Skip to main content

ironwork_rt/module/
mod.rs

1//! The load module (`.iwm`) of docs/load-module.md: its container, encoding rules and codec.
2
3pub mod codec;
4mod container;
5pub mod crc;
6pub mod leb;
7mod programs;
8mod strings;
9
10use std::fmt;
11
12pub use codec::{Decode, Encode, Reader, Writer};
13pub use container::{EXTENSIONS, MAGIC, Module, ModuleWriter, OPTIONAL, Section, SectionEntry, Version};
14pub use programs::{DirectoryEntry, LoadedModule, read, write, write_with};
15pub use strings::StringTable;
16
17#[derive(Clone, Debug, PartialEq, Eq)]
18pub enum ModuleError {
19    /// The file does not start with the magic.
20    NotAModule,
21    /// A format version this reader does not read (ยง8.1).
22    Version(Version),
23    /// Required feature bits this reader does not know.
24    Feature(u32),
25    /// The file is shorter than its header, or than the length its header gives.
26    Truncated {
27        expected: u64,
28        actual: u64,
29    },
30    /// The file is longer than the length its header gives.
31    TrailingBytes {
32        expected: u64,
33        actual: u64,
34    },
35    HeaderChecksum {
36        computed: u32,
37        stored: u32,
38    },
39    SectionChecksum {
40        id: u32,
41        computed: u32,
42        stored: u32,
43    },
44    /// A section id this reader does not know, without the optional flag.
45    UnknownSection(u32),
46    MissingSection(u32),
47    /// `offset` counts from the start of the file in the header and table, else of the section body.
48    Malformed {
49        section: &'static str,
50        offset: usize,
51        reason: String,
52    },
53}
54
55fn section_name(id: u32) -> String {
56    Section::by_id(id).map_or_else(|| format!("{id:#x}"), |s| s.name.to_owned())
57}
58
59impl fmt::Display for ModuleError {
60    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61        match self {
62            Self::NotAModule => write!(f, "not an ironwork load module"),
63            Self::Version(found) => {
64                let reads = Version::CURRENT;
65                write!(f, "load module format {}.{}; this ironwork reads ", found.major, found.minor)?;
66                if reads.major == 0 {
67                    write!(f, "0.{}", reads.minor)?
68                } else {
69                    write!(f, "{}.x", reads.major)?
70                }
71                write!(f, ". Compile the source again")
72            }
73            Self::Feature(bits) => write!(f, "load module needs features {bits:#010x}, which this ironwork lacks"),
74            Self::Truncated { expected, actual } => write!(f, "truncated: {actual} bytes of {expected}"),
75            Self::TrailingBytes { expected, actual } => {
76                write!(f, "{} bytes after the end of the module at {expected}", actual.saturating_sub(*expected))
77            }
78            Self::HeaderChecksum { computed, stored } => {
79                write!(f, "header is corrupt (checksum {computed:08X}, expected {stored:08X})")
80            }
81            Self::SectionChecksum { id, computed, stored } => {
82                write!(f, "section {} is corrupt (checksum {computed:08X}, expected {stored:08X})", section_name(*id))
83            }
84            Self::UnknownSection(id) => write!(f, "required section {id:#x} is unknown to this ironwork"),
85            Self::MissingSection(id) => write!(f, "required section {} is missing", section_name(*id)),
86            Self::Malformed { section, offset, reason } => {
87                write!(f, "{section} is malformed at byte {offset}: {reason}")
88            }
89        }
90    }
91}
92
93impl std::error::Error for ModuleError {}