diff --git a/crates/xrpl-host-functions/src/lib.rs b/crates/xrpl-host-functions/src/lib.rs index 80d104301b..06db3c6c59 100644 --- a/crates/xrpl-host-functions/src/lib.rs +++ b/crates/xrpl-host-functions/src/lib.rs @@ -5,67 +5,21 @@ //! wasm engine registers from. //! //! The split: hand-written here is the vocabulary the declarations are written in — -//! [`HostError`], [`HostResult`], [`HASH_LEN`] — and everything derived from the -//! declarations is generated. The expansion names nothing this file does not, so the -//! two sides meet only in the block below. +//! [`HostError`], [`TraceDataType`], [`HostResult`], [`HASH_LEN`] — and everything +//! derived from the declarations is generated. The expansion names nothing this file +//! does not, so the two sides meet only in the block below. +//! +//! So this file is lists — error codes, trace data types, functions. The `macro_rules!` +//! that expand the first two into enums live in `macros.rs`. #![no_std] +#[macro_use] +mod macros; + // Not re-exported: the ABI is declared once, here, and this is the only call site. use xrpl_host_functions_macros::host_functions; -/// Declares [`HostError`] from one list: the variants, [`HostError::ALL`] and -/// [`HostError::from_code`]'s table all expand from the codes below. -/// -/// One list is what makes `ALL` complete. Rust cannot enumerate an enum's -/// variants — an exhaustive `match` forces an arm per variant but gives nothing to -/// iterate — so a hand-written `ALL` beside a hand-written enum could only be kept -/// in step by review, and `ALL`'s whole purpose is to be the set a test can trust. -/// A code added below gains its `ALL` entry and its `from_code` arm by -/// construction. `HostFunctionSpec::ALL` is complete the same way, from the -/// `host_functions!` block. -macro_rules! host_errors { - ($($variant:ident = $code:literal,)+) => { - /// Error codes a host function may return. - /// - /// The discriminants mirror `HostFunctionError` in - /// `include/xrpl/tx/wasm/WasmCommon.h`, so a negative `i32` crossing the wasm - /// boundary means the same thing to the guest, the Rust host, and the existing - /// C++ code. The full set is kept (not just the ones the PoC uses today) to - /// preserve that shared meaning. - #[derive(Debug, Clone, Copy, PartialEq, Eq)] - #[repr(i32)] - pub enum HostError { - $($variant = $code,)+ - } - - impl HostError { - /// Every error a host function may return, in code order. - /// - /// The complete set, and complete by construction: a wasm engine's - /// split between the codes it hands the guest and the conditions it - /// traps on is a decision per variant, so the test that checks the - /// split iterates this and a code added to the ABI cannot slip past it. - pub const ALL: &'static [HostError] = &[$(HostError::$variant,)+]; - - /// The negative wire value the guest sees as the function's return code. - #[inline] - pub const fn code(self) -> i32 { - self as i32 - } - - /// Reconstruct a `HostError` from its wire code; unknown/positive values - /// map to `Internal`. - pub const fn from_code(code: i32) -> HostError { - match code { - $($code => HostError::$variant,)+ - _ => HostError::Internal, - } - } - } - }; -} - host_errors! { Internal = -1, FieldNotFound = -2, @@ -98,49 +52,6 @@ pub type HostResult = Result; /// A `sha512Half` digest: the first 32 bytes of a SHA-512, as XRPL uses it. pub const HASH_LEN: usize = 32; -/// Declares [`TraceDataType`] from one list, so [`TraceDataType::ALL`], -/// [`TraceDataType::code`] and [`TraceDataType::from_code`] cannot fall behind the -/// variants — the reason `host_errors!` above is written this way. -macro_rules! trace_data_types { - ($($(#[$doc:meta])* $variant:ident = $code:literal,)+) => { - /// How [`HostFunctions::trace`] is to read its data buffer. - /// - /// The discriminants are wire values shared with the guest stdlib: append only, - /// never renumber. They start at 1, so a zeroed argument names no type rather - /// than the first one. - /// - /// This is the declaration a guest and a host both compile against. The host - /// side needs a second one — `cxx` cannot be a dependency here, since this - /// crate also links into the guest — so `xrpl-wasm-vm-ffi` declares a shared - /// enum for C++ and converts, exhaustively, from this. - #[derive(Debug, Clone, Copy, PartialEq, Eq)] - #[repr(i32)] - pub enum TraceDataType { - $($(#[$doc])* $variant = $code,)+ - } - - impl TraceDataType { - /// Every data type a guest may name, in code order. - pub const ALL: &'static [TraceDataType] = &[$(TraceDataType::$variant,)+]; - - /// The wire value a guest passes to name this type. - #[inline] - pub const fn code(self) -> i32 { - self as i32 - } - - /// The type `code` names, or `None`: the engine drops a call it cannot - /// read rather than guessing at a rendering the guest did not ask for. - pub const fn from_code(code: i32) -> Option { - match code { - $($code => Some(TraceDataType::$variant),)+ - _ => None, - } - } - } - }; -} - trace_data_types! { /// 8 little-endian bytes, rendered as a signed decimal. Int64 = 1, diff --git a/crates/xrpl-host-functions/src/macros.rs b/crates/xrpl-host-functions/src/macros.rs new file mode 100644 index 0000000000..d720cd2ddc --- /dev/null +++ b/crates/xrpl-host-functions/src/macros.rs @@ -0,0 +1,101 @@ +//! The `macro_rules!` behind the two hand-listed enums, [`crate::HostError`] and +//! [`crate::TraceDataType`]. +//! +//! Each takes one list of `Variant = code,` and expands the enum together with the +//! `ALL`/`code`/`from_code` set that must not fall behind it. The lists themselves stay +//! in `lib.rs`, beside the `host_functions!` block. + +/// Declares [`crate::HostError`] from one list: the variants, `HostError::ALL` and +/// `HostError::from_code`'s table all expand from the codes given. +/// +/// One list is what makes `ALL` complete. Rust cannot enumerate an enum's +/// variants — an exhaustive `match` forces an arm per variant but gives nothing to +/// iterate — so a hand-written `ALL` beside a hand-written enum could only be kept +/// in step by review, and `ALL`'s whole purpose is to be the set a test can trust. +/// A code added to the list gains its `ALL` entry and its `from_code` arm by +/// construction. `HostFunctionSpec::ALL` is complete the same way, from the +/// `host_functions!` block. +macro_rules! host_errors { + ($($variant:ident = $code:literal,)+) => { + /// Error codes a host function may return. + /// + /// The discriminants mirror `HostFunctionError` in + /// `include/xrpl/tx/wasm/WasmCommon.h`, so a negative `i32` crossing the wasm + /// boundary means the same thing to the guest, the Rust host, and the existing + /// C++ code. The full set is kept (not just the ones the PoC uses today) to + /// preserve that shared meaning. + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + #[repr(i32)] + pub enum HostError { + $($variant = $code,)+ + } + + impl HostError { + /// Every error a host function may return, in code order. + /// + /// The complete set, and complete by construction: a wasm engine's + /// split between the codes it hands the guest and the conditions it + /// traps on is a decision per variant, so the test that checks the + /// split iterates this and a code added to the ABI cannot slip past it. + pub const ALL: &'static [HostError] = &[$(HostError::$variant,)+]; + + /// The negative wire value the guest sees as the function's return code. + #[inline] + pub const fn code(self) -> i32 { + self as i32 + } + + /// Reconstruct a `HostError` from its wire code; unknown/positive values + /// map to `Internal`. + pub const fn from_code(code: i32) -> HostError { + match code { + $($code => HostError::$variant,)+ + _ => HostError::Internal, + } + } + } + }; +} + +/// Declares [`crate::TraceDataType`] from one list, so `TraceDataType::ALL`, +/// `TraceDataType::code` and `TraceDataType::from_code` cannot fall behind the +/// variants — the reason `host_errors!` above is written this way. +macro_rules! trace_data_types { + ($($(#[$doc:meta])* $variant:ident = $code:literal,)+) => { + /// How [`HostFunctions::trace`] is to read its data buffer. + /// + /// The discriminants are wire values shared with the guest stdlib: append only, + /// never renumber. They start at 1, so a zeroed argument names no type rather + /// than the first one. + /// + /// This is the declaration a guest and a host both compile against. The host + /// side needs a second one — `cxx` cannot be a dependency here, since this + /// crate also links into the guest — so `xrpl-wasm-vm-ffi` declares a shared + /// enum for C++ and converts, exhaustively, from this. + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + #[repr(i32)] + pub enum TraceDataType { + $($(#[$doc])* $variant = $code,)+ + } + + impl TraceDataType { + /// Every data type a guest may name, in code order. + pub const ALL: &'static [TraceDataType] = &[$(TraceDataType::$variant,)+]; + + /// The wire value a guest passes to name this type. + #[inline] + pub const fn code(self) -> i32 { + self as i32 + } + + /// The type `code` names, or `None`: the engine drops a call it cannot + /// read rather than guessing at a rendering the guest did not ask for. + pub const fn from_code(code: i32) -> Option { + match code { + $($code => Some(TraceDataType::$variant),)+ + _ => None, + } + } + } + }; +}