Skip to main content

firmion_extension/
lib.rs

1// Extension API for the Firmion binary image compiler.
2//
3// This crate defines the public API for Firmion extension authors.
4//
5// FirmionExtension -- the single trait all extensions implement.
6//
7// ExtArg -- typed argument passed to execute().  Each parameter passed
8// to an extension in Firmion code maps to one ExtArg passed to the extension
9// by the compiler.
10//
11// Argument types:
12//   Int(u64)                     -- numeric expression (u64, i64, or integer const)
13//   Str(&str)                    -- quoted string const
14//   Slice { start, len, data }   -- Data slice from the in-flight output data.
15//                                   Firmion automatically converts sections names
16//                                   into slices.
17//
18// Args may appear in any order.  Firmion resolves the section name to its
19// location and provides the image bytes without extension authors having to
20// compute offsets.
21//
22// Firmion registers extensions at startup via the ExtensionRegistry.
23// Firmion calls size() exactly once during registration and caches
24// the result.  The `firmion` and `std` namespaces are reserved.
25//
26// Named parameters:
27//   An extension declares named parameters via params(), returning a slice of
28//   ParamDesc values.  Each ParamDesc pairs a name with a ParamKind.
29//   ParamKind::Slice declares that a parameter accepts a sequence of bytes
30//   (supplied at call sites as a section name).  ParamKind::Int and
31//   ParamKind::Str declare numeric and string parameters respectively.
32
33// Don't clutter upstream docs.rs for an otherwise private library.
34#![doc(hidden)]
35
36pub mod extension_registry;
37pub mod test_mocks;
38
39/// The kind of a declared extension parameter.
40///
41/// Used in [`ParamDesc`] to specify what type of argument a parameter accepts.
42#[derive(Debug, Clone, Copy, PartialEq, Eq)]
43pub enum ParamKind {
44    /// A numeric argument: u64, i64, or an untyped integer constant.
45    Int,
46    /// A quoted string constant.
47    Str,
48    /// A sequence of bytes with starting offset and length.
49    Slice,
50}
51
52/// Describes one declared parameter of an extension.
53///
54/// An extension returns a slice of `ParamDesc` from [`FirmionExtension::params`]
55/// to opt into named-argument call sites.
56#[derive(Debug, Clone, Copy)]
57pub struct ParamDesc {
58    /// The parameter name used at call sites: `foo::bar(name=value)`.
59    pub name: &'static str,
60    /// The kind of value the parameter accepts.
61    pub kind: ParamKind,
62}
63
64/// The argument type passed to an extension at execution time.
65///
66/// Each Firmion call-site argument produces exactly one `ExtArg`.
67#[derive(Debug)]
68pub enum ParamArg<'a> {
69    /// A numeric argument: u64, i64, or an untyped integer constant.
70    Int(u64),
71    /// A quoted string constant.
72    Str(&'a str),
73    /// A slice which includes an immutable slice of the output data.
74    Slice {
75        /// Read-only view of the output image at `start..start+len`.
76        data: &'a [u8],
77    },
78}
79
80/// Implement this trait to create a Firmion extension.
81///
82/// An extension writes a fixed number of bytes into the output image.
83/// Arguments arrive as typed [`ExtArg`] values corresponding 1:1 to the
84/// Firmion call-site arguments.  A section name argument delivers the image
85/// bytes at that section directly in the [`ExtArg::Section`] variant.
86///
87/// # Example: numeric argument
88///
89/// ```rust
90/// use firmion_extension::{FirmionExtension, ParamArg};
91///
92/// pub struct MyCrc;
93///
94/// impl FirmionExtension for MyCrc {
95///     fn name(&self) -> &str { "my_org::crc" }
96///     fn size(&self) -> usize { 4 }
97///     fn execute<'a>(&self, args: &[ParamArg<'a>], out: &mut [u8]) -> Result<(), String> {
98///         let val = match args.first() {
99///             Some(ParamArg::Int(v)) => *v as u32,
100///             _ => return Err("Expected one numeric argument".to_string()),
101///         };
102///         out.copy_from_slice(&val.to_be_bytes());
103///         Ok(())
104///     }
105/// }
106/// ```
107///
108/// # Example: section argument
109///
110/// ```rust
111/// use firmion_extension::{FirmionExtension, ParamArg};
112///
113/// pub struct MyChecksum;
114///
115/// impl FirmionExtension for MyChecksum {
116///     fn name(&self) -> &str { "my_org::checksum" }
117///     fn size(&self) -> usize { 8 }
118///     fn execute<'a>(&self, args: &[ParamArg<'a>], out: &mut [u8]) -> Result<(), String> {
119///         let data = match args.first() {
120///             Some(ParamArg::Slice { data }) => *data,
121///             _ => return Err("Expected a slice argument".to_string()),
122///         };
123///         let sum: u64 = data.iter().map(|&b| b as u64).sum();
124///         out.copy_from_slice(&sum.to_le_bytes());
125///         Ok(())
126///     }
127/// }
128/// ```
129pub trait FirmionExtension {
130    /// Returns the namespace-qualified name used to invoke the extension.
131    /// For example, `"my_org::crc"`. The `firmion` and `std` namespaces are
132    /// reserved for internal use.
133    fn name(&self) -> &str;
134
135    /// Returns the exact number of bytes the extension writes to `out`.
136    ///
137    /// Firmion calls this method once at registration and caches the result.
138    /// All layout calculations use the cached value.
139    fn size(&self) -> usize;
140
141    /// Returns the declared parameters for this extension.
142    ///
143    /// Each entry in the slice declares one parameter: its name and kind.
144    /// Returns the declared parameters for this extension.
145    ///
146    /// Each entry declares one parameter: its name and kind.  Firmion validates
147    /// argument names against this slice and reorders call-site args to
148    /// declaration order before passing them to [`execute`](Self::execute).
149    fn params(&self) -> &[ParamDesc] {
150        &[]
151    }
152
153    /// Produces the extension output bytes.
154    ///
155    /// * `args` -- one [`ExtArg`] per Firmion call-site argument, in declaration order.
156    /// * `out`  -- pre-allocated buffer of exactly [`size`](Self::size) bytes.
157    ///
158    /// Return `Err(message)` to abort compilation with a diagnostic.
159    fn execute<'a>(&self, args: &[ParamArg<'a>], out: &mut [u8]) -> Result<(), String>;
160}