Skip to main content

xrpl_wasm_stdlib/fields/
decoder.rs

1//! Field decoding traits for XRPL transaction and ledger object fields.
2//!
3//! This module defines a lightweight, host-independent decoding contract for types that
4//! are constructed directly from a raw byte buffer.
5//!
6//! # Examples
7//!
8//! ```
9//! use xrpl_wasm_stdlib::fields::decoder::{FieldDecoder, FromCurrentTx};
10//! use xrpl_wasm_stdlib::host::Error;
11//!
12//! /// A toy field: a single boolean flag byte (0 or 1).
13//! struct Flag(bool);
14//!
15//! impl FieldDecoder for Flag {
16//!     type Buffer = [u8; 1];
17//!
18//!     fn decode(bytes: &[u8]) -> Result<Self, Error> {
19//!         match bytes {
20//!             [0] => Ok(Flag(false)),
21//!             [1] => Ok(Flag(true)),
22//!             _ => Err(Error::InvalidDecoding),
23//!         }
24//!     }
25//! }
26//!
27//! // Declares that `Flag` can be decoded from the currently executing transaction.
28//! // `FromLedger` is implemented the same way for fields readable from ledger objects.
29//! impl FromCurrentTx for Flag {}
30//!
31//! let flag = Flag::decode(&[1]).unwrap();
32//! assert!(flag.0);
33//!
34//! assert!(Flag::decode(&[2]).is_err());
35//! ```
36
37use crate::host::Error;
38
39/// Decodes a fixed-format XRPL field from its raw byte representation.
40pub trait FieldDecoder: Sized {
41    /// A stack buffer sized to hold this field's raw bytes, used by generic host-field
42    /// getters to read into before calling [`decode`](FieldDecoder::decode).
43    type Buffer: AsMut<[u8]> + Default;
44
45    /// Decodes `Self` from `bytes`, returning an error if the bytes are malformed.
46    fn decode(bytes: &[u8]) -> Result<Self, Error>;
47}
48
49/// Marker trait for fields that can be decoded from the currently executing transaction.
50pub trait FromCurrentTx: FieldDecoder {}
51
52/// Marker trait for fields that can be decoded from a ledger object.
53pub trait FromLedger: FieldDecoder {}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58
59    #[derive(Debug, PartialEq, Eq)]
60    struct TxOnly(u8);
61
62    impl FieldDecoder for TxOnly {
63        type Buffer = [u8; 1];
64
65        fn decode(bytes: &[u8]) -> Result<Self, Error> {
66            bytes
67                .first()
68                .copied()
69                .map(TxOnly)
70                .ok_or(Error::FieldNotFound)
71        }
72    }
73    impl FromCurrentTx for TxOnly {}
74
75    #[derive(Debug, PartialEq, Eq)]
76    struct ObjOnly(u8);
77
78    impl FieldDecoder for ObjOnly {
79        type Buffer = [u8; 1];
80
81        fn decode(bytes: &[u8]) -> Result<Self, Error> {
82            bytes
83                .first()
84                .copied()
85                .map(ObjOnly)
86                .ok_or(Error::FieldNotFound)
87        }
88    }
89    impl FromLedger for ObjOnly {}
90
91    #[derive(Debug, PartialEq, Eq)]
92    struct TxAndObj(u8);
93
94    impl FieldDecoder for TxAndObj {
95        type Buffer = [u8; 1];
96
97        fn decode(bytes: &[u8]) -> Result<Self, Error> {
98            bytes
99                .first()
100                .copied()
101                .map(TxAndObj)
102                .ok_or(Error::FieldNotFound)
103        }
104    }
105    impl FromCurrentTx for TxAndObj {}
106    impl FromLedger for TxAndObj {}
107
108    // These take no arguments and are never called; if a type didn't actually implement
109    // the trait, the crate would fail to compile.
110    fn assert_from_current_tx<T: FromCurrentTx>() {}
111    fn assert_from_ledger<T: FromLedger>() {}
112
113    #[test]
114    fn tx_only_implements_from_current_tx_only() {
115        assert_from_current_tx::<TxOnly>();
116    }
117
118    #[test]
119    fn obj_only_implements_from_ledger_only() {
120        assert_from_ledger::<ObjOnly>();
121    }
122
123    #[test]
124    fn tx_and_obj_implements_both() {
125        assert_from_current_tx::<TxAndObj>();
126        assert_from_ledger::<TxAndObj>();
127    }
128
129    #[test]
130    fn decode_returns_value_on_success() {
131        let decoded = TxOnly::decode(&[42]).unwrap();
132        assert_eq!(decoded, TxOnly(42));
133    }
134
135    #[test]
136    fn decode_returns_error_on_empty_input() {
137        let result = TxOnly::decode(&[]);
138        assert!(result.is_err());
139        assert_eq!(result.unwrap_err().code(), Error::FieldNotFound.code());
140    }
141
142    #[test]
143    fn buffer_type_has_expected_length() {
144        let mut buffer = <TxOnly as FieldDecoder>::Buffer::default();
145        assert_eq!(buffer.as_mut().len(), 1);
146    }
147}