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