xrpl_wasm_stdlib/core/ledger_objects/
mod.rs

1pub mod account_root;
2pub mod current_escrow;
3pub mod escrow;
4pub mod traits;
5
6use crate::core::types::account_id::{ACCOUNT_ID_SIZE, AccountID};
7use crate::core::types::amount::{AMOUNT_SIZE, Amount};
8use crate::core::types::blob::Blob;
9use crate::core::types::currency::{CURRENCY_SIZE, Currency};
10use crate::core::types::issue::Issue;
11use crate::core::types::uint::{HASH128_SIZE, HASH256_SIZE, Hash128, Hash256};
12use crate::host::error_codes::{
13    match_result_code_with_expected_bytes, match_result_code_with_expected_bytes_optional,
14};
15use crate::host::field_helpers::{
16    get_fixed_size_field_with_expected_bytes, get_fixed_size_field_with_expected_bytes_optional,
17    get_variable_size_field, get_variable_size_field_optional,
18};
19use crate::host::{Result, get_current_ledger_obj_field, get_ledger_obj_field};
20
21/// Trait for types that can be retrieved from ledger object fields.
22///
23/// This trait provides a unified interface for retrieving typed data from XRPL ledger objects,
24/// replacing the previous collection of type-specific functions with a generic, type-safe approach.
25///
26/// ## Supported Types
27///
28/// The following types implement this trait:
29/// - `u16` - 16-bit unsigned integers (2 bytes)
30/// - `u32` - 32-bit unsigned integers (4 bytes)
31/// - `u64` - 64-bit unsigned integers (8 bytes)
32/// - `AccountID` - 20-byte account identifiers
33/// - `Amount` - XRP amounts and token amounts (variable size, up to 48 bytes)
34/// - `Hash128` - 128-bit cryptographic hashes (16 bytes)
35/// - `Hash256` - 256-bit cryptographic hashes (32 bytes)
36/// - `Blob<N>` - Variable-length binary data (generic over buffer size `N`)
37///
38/// ## Usage Patterns
39///
40/// ```rust,no_run
41/// use xrpl_wasm_stdlib::core::ledger_objects::{ledger_object, current_ledger_object};
42/// use xrpl_wasm_stdlib::core::types::account_id::AccountID;
43/// use xrpl_wasm_stdlib::sfield;
44///
45/// fn example() {
46///   let slot = 0;
47///   // Get a required field from a specific ledger object
48///   let balance: u64 = ledger_object::get_field(slot, sfield::Balance).unwrap();
49///   let account: AccountID = ledger_object::get_field(slot, sfield::Account).unwrap();
50///
51///   // Get an optional field from the current ledger object
52///   let flags: Option<u32> = current_ledger_object::get_field_optional(sfield::Flags).unwrap();
53/// }
54/// ```
55///
56/// ## Error Handling
57///
58/// - Required field methods return `Result<T>` and error if the field is missing
59/// - Optional field methods return `Result<Option<T>>` and return `None` if the field is missing
60/// - All methods return appropriate errors for buffer size mismatches or other retrieval failures
61///
62/// ## Safety Considerations
63///
64/// - All implementations use appropriately sized buffers for their data types
65/// - Buffer sizes are validated against expected field sizes where applicable
66/// - Unsafe operations are contained within the host function calls
67pub trait FieldGetter: Sized {
68    /// Get a required field from the current ledger object.
69    ///
70    /// # Arguments
71    ///
72    /// * `field_code` - The field code identifying which field to retrieve
73    ///
74    /// # Returns
75    ///
76    /// Returns a `Result<Self>` where:
77    /// * `Ok(Self)` - The field value for the specified field
78    /// * `Err(Error)` - If the field cannot be retrieved or has unexpected size
79    fn get_from_current_ledger_obj(field_code: i32) -> Result<Self>;
80
81    /// Get an optional field from the current ledger object.
82    ///
83    /// # Arguments
84    ///
85    /// * `field_code` - The field code identifying which field to retrieve
86    ///
87    /// # Returns
88    ///
89    /// Returns a `Result<Option<Self>>` where:
90    /// * `Ok(Some(Self))` - The field value for the specified field
91    /// * `Ok(None)` - If the field is not present
92    /// * `Err(Error)` - If the field cannot be retrieved or has unexpected size
93    fn get_from_current_ledger_obj_optional(field_code: i32) -> Result<Option<Self>>;
94
95    /// Get a required field from a specific ledger object.
96    ///
97    /// # Arguments
98    ///
99    /// * `register_num` - The register number holding the ledger object
100    /// * `field_code` - The field code identifying which field to retrieve
101    ///
102    /// # Returns
103    ///
104    /// Returns a `Result<Self>` where:
105    /// * `Ok(Self)` - The field value for the specified field
106    /// * `Err(Error)` - If the field cannot be retrieved or has unexpected size
107    fn get_from_ledger_obj(register_num: i32, field_code: i32) -> Result<Self>;
108
109    /// Get an optional field from a specific ledger object.
110    ///
111    /// # Arguments
112    ///
113    /// * `register_num` - The register number holding the ledger object
114    /// * `field_code` - The field code identifying which field to retrieve
115    ///
116    /// # Returns
117    ///
118    /// Returns a `Result<Option<Self>>` where:
119    /// * `Ok(Some(Self))` - The field value for the specified field
120    /// * `Ok(None)` - If the field is not present in the ledger object
121    /// * `Err(Error)` - If the field retrieval operation failed
122    fn get_from_ledger_obj_optional(register_num: i32, field_code: i32) -> Result<Option<Self>>;
123}
124
125/// Trait for types that can be retrieved as fixed-size fields from ledger objects.
126///
127/// This trait enables a generic implementation of `FieldGetter` for all fixed-size
128/// unsigned integer types (u8, u16, u32, u64). Types implementing this trait must
129/// have a known, constant size in bytes.
130///
131/// # Implementing Types
132///
133/// - `u8` - 1 byte
134/// - `u16` - 2 bytes
135/// - `u32` - 4 bytes
136/// - `u64` - 8 bytes
137trait FixedSizeFieldType: Sized {
138    /// The size of this type in bytes
139    const SIZE: usize;
140}
141
142impl FixedSizeFieldType for u8 {
143    const SIZE: usize = 1;
144}
145
146impl FixedSizeFieldType for u16 {
147    const SIZE: usize = 2;
148}
149
150impl FixedSizeFieldType for u32 {
151    const SIZE: usize = 4;
152}
153
154impl FixedSizeFieldType for u64 {
155    const SIZE: usize = 8;
156}
157
158/// Generic implementation of `FieldGetter` for all fixed-size unsigned integer types.
159///
160/// This single implementation handles u8, u16, u32, and u64 by leveraging the
161/// `FixedSizeFieldType` trait. The implementation:
162/// - Allocates a buffer of the appropriate size
163/// - Calls the host function to retrieve the field
164/// - Validates that the returned byte count matches the expected size
165/// - Converts the buffer to the target type
166///
167/// # Buffer Management
168///
169/// Uses `MaybeUninit` for efficient stack allocation without initialization overhead.
170/// The buffer size is determined at compile-time via the `SIZE` constant.
171impl<T: FixedSizeFieldType> FieldGetter for T {
172    #[inline]
173    fn get_from_current_ledger_obj(field_code: i32) -> Result<Self> {
174        let mut value = core::mem::MaybeUninit::<T>::uninit();
175        let result_code =
176            unsafe { get_current_ledger_obj_field(field_code, value.as_mut_ptr().cast(), T::SIZE) };
177        match_result_code_with_expected_bytes(result_code, T::SIZE, || unsafe {
178            value.assume_init()
179        })
180    }
181
182    #[inline]
183    fn get_from_current_ledger_obj_optional(field_code: i32) -> Result<Option<Self>> {
184        let mut value = core::mem::MaybeUninit::<T>::uninit();
185        let result_code =
186            unsafe { get_current_ledger_obj_field(field_code, value.as_mut_ptr().cast(), T::SIZE) };
187        match_result_code_with_expected_bytes_optional(result_code, T::SIZE, || {
188            Some(unsafe { value.assume_init() })
189        })
190    }
191
192    #[inline]
193    fn get_from_ledger_obj(register_num: i32, field_code: i32) -> Result<Self> {
194        let mut value = core::mem::MaybeUninit::<T>::uninit();
195        let result_code = unsafe {
196            get_ledger_obj_field(register_num, field_code, value.as_mut_ptr().cast(), T::SIZE)
197        };
198        match_result_code_with_expected_bytes(result_code, T::SIZE, || unsafe {
199            value.assume_init()
200        })
201    }
202
203    #[inline]
204    fn get_from_ledger_obj_optional(register_num: i32, field_code: i32) -> Result<Option<Self>> {
205        let mut value = core::mem::MaybeUninit::<T>::uninit();
206        let result_code = unsafe {
207            get_ledger_obj_field(register_num, field_code, value.as_mut_ptr().cast(), T::SIZE)
208        };
209        match_result_code_with_expected_bytes_optional(result_code, T::SIZE, || {
210            Some(unsafe { value.assume_init() })
211        })
212    }
213}
214
215/// Implementation of `FieldGetter` for XRPL account identifiers.
216///
217/// This implementation handles 20-byte account ID fields in XRPL ledger objects.
218/// Account IDs uniquely identify accounts on the XRPL network and are derived
219/// from public keys using cryptographic hashing.
220///
221/// # Buffer Management
222///
223/// Uses a 20-byte buffer (ACCOUNT_ID_SIZE) and validates that exactly 20 bytes
224/// are returned from the host function. The buffer is converted to an AccountID
225/// using the `From<[u8; 20]>` implementation.
226impl FieldGetter for AccountID {
227    #[inline]
228    fn get_from_current_ledger_obj(field_code: i32) -> Result<Self> {
229        match get_fixed_size_field_with_expected_bytes::<ACCOUNT_ID_SIZE, _>(
230            field_code,
231            |fc, buf, size| unsafe { get_current_ledger_obj_field(fc, buf, size) },
232        ) {
233            Result::Ok(buffer) => Result::Ok(buffer.into()),
234            Result::Err(e) => Result::Err(e),
235        }
236    }
237
238    #[inline]
239    fn get_from_current_ledger_obj_optional(field_code: i32) -> Result<Option<Self>> {
240        match get_fixed_size_field_with_expected_bytes_optional::<ACCOUNT_ID_SIZE, _>(
241            field_code,
242            |fc, buf, size| unsafe { get_current_ledger_obj_field(fc, buf, size) },
243        ) {
244            Result::Ok(buffer) => Result::Ok(buffer.map(|b| b.into())),
245            Result::Err(e) => Result::Err(e),
246        }
247    }
248
249    #[inline]
250    fn get_from_ledger_obj(register_num: i32, field_code: i32) -> Result<Self> {
251        match get_fixed_size_field_with_expected_bytes::<ACCOUNT_ID_SIZE, _>(
252            field_code,
253            |fc, buf, size| unsafe { get_ledger_obj_field(register_num, fc, buf, size) },
254        ) {
255            Result::Ok(buffer) => Result::Ok(buffer.into()),
256            Result::Err(e) => Result::Err(e),
257        }
258    }
259
260    #[inline]
261    fn get_from_ledger_obj_optional(register_num: i32, field_code: i32) -> Result<Option<Self>> {
262        match get_fixed_size_field_with_expected_bytes_optional::<ACCOUNT_ID_SIZE, _>(
263            field_code,
264            |fc, buf, size| unsafe { get_ledger_obj_field(register_num, fc, buf, size) },
265        ) {
266            Result::Ok(buffer) => Result::Ok(buffer.map(|b| b.into())),
267            Result::Err(e) => Result::Err(e),
268        }
269    }
270}
271
272/// Implementation of `FieldGetter` for XRPL amount values.
273///
274/// This implementation handles amount fields in XRPL ledger objects, which can represent
275/// either XRP amounts (8 bytes) or token amounts (up to 48 bytes including currency code
276/// and issuer information).
277///
278/// # Buffer Management
279///
280/// Uses a 48-byte buffer to accommodate the largest possible amount representation.
281/// The Amount type handles the parsing of different amount formats internally.
282/// No strict byte count validation is performed since amounts can vary in size.
283impl FieldGetter for Amount {
284    #[inline]
285    fn get_from_current_ledger_obj(field_code: i32) -> Result<Self> {
286        match get_variable_size_field::<AMOUNT_SIZE, _>(field_code, |fc, buf, size| unsafe {
287            get_current_ledger_obj_field(fc, buf, size)
288        }) {
289            Result::Ok((buffer, _len)) => Result::Ok(Amount::from(buffer)),
290            Result::Err(e) => Result::Err(e),
291        }
292    }
293
294    #[inline]
295    fn get_from_current_ledger_obj_optional(field_code: i32) -> Result<Option<Self>> {
296        match get_variable_size_field_optional::<AMOUNT_SIZE, _>(
297            field_code,
298            |fc, buf, size| unsafe { get_current_ledger_obj_field(fc, buf, size) },
299        ) {
300            Result::Ok(opt) => Result::Ok(opt.map(|(buffer, _len)| Amount::from(buffer))),
301            Result::Err(e) => Result::Err(e),
302        }
303    }
304
305    #[inline]
306    fn get_from_ledger_obj(register_num: i32, field_code: i32) -> Result<Self> {
307        match get_variable_size_field::<AMOUNT_SIZE, _>(field_code, |fc, buf, size| unsafe {
308            get_ledger_obj_field(register_num, fc, buf, size)
309        }) {
310            Result::Ok((buffer, _len)) => Result::Ok(Amount::from(buffer)),
311            Result::Err(e) => Result::Err(e),
312        }
313    }
314
315    #[inline]
316    fn get_from_ledger_obj_optional(register_num: i32, field_code: i32) -> Result<Option<Self>> {
317        match get_variable_size_field_optional::<AMOUNT_SIZE, _>(
318            field_code,
319            |fc, buf, size| unsafe { get_ledger_obj_field(register_num, fc, buf, size) },
320        ) {
321            Result::Ok(opt) => Result::Ok(opt.map(|(buffer, _len)| Amount::from(buffer))),
322            Result::Err(e) => Result::Err(e),
323        }
324    }
325}
326
327/// Implementation of `FieldGetter` for 128-bit cryptographic hashes.
328///
329/// This implementation handles 16-byte hash fields in XRPL ledger objects.
330/// Hash128 values are commonly used for shorter identifiers and checksums
331/// in XRPL, such as email hashes.
332///
333/// # Buffer Management
334///
335/// Uses a 16-byte buffer (HASH128_SIZE) and validates that exactly 16 bytes
336/// are returned from the host function to ensure data integrity.
337impl FieldGetter for Hash128 {
338    #[inline]
339    fn get_from_current_ledger_obj(field_code: i32) -> Result<Self> {
340        match get_fixed_size_field_with_expected_bytes::<HASH128_SIZE, _>(
341            field_code,
342            |fc, buf, size| unsafe { get_current_ledger_obj_field(fc, buf, size) },
343        ) {
344            Result::Ok(buffer) => Result::Ok(buffer.into()),
345            Result::Err(e) => Result::Err(e),
346        }
347    }
348
349    #[inline]
350    fn get_from_current_ledger_obj_optional(field_code: i32) -> Result<Option<Self>> {
351        match get_fixed_size_field_with_expected_bytes_optional::<HASH128_SIZE, _>(
352            field_code,
353            |fc, buf, size| unsafe { get_current_ledger_obj_field(fc, buf, size) },
354        ) {
355            Result::Ok(buffer) => Result::Ok(buffer.map(|b| b.into())),
356            Result::Err(e) => Result::Err(e),
357        }
358    }
359
360    #[inline]
361    fn get_from_ledger_obj(register_num: i32, field_code: i32) -> Result<Self> {
362        match get_fixed_size_field_with_expected_bytes::<HASH128_SIZE, _>(
363            field_code,
364            |fc, buf, size| unsafe { get_ledger_obj_field(register_num, fc, buf, size) },
365        ) {
366            Result::Ok(buffer) => Result::Ok(buffer.into()),
367            Result::Err(e) => Result::Err(e),
368        }
369    }
370
371    #[inline]
372    fn get_from_ledger_obj_optional(register_num: i32, field_code: i32) -> Result<Option<Self>> {
373        match get_fixed_size_field_with_expected_bytes_optional::<HASH128_SIZE, _>(
374            field_code,
375            |fc, buf, size| unsafe { get_ledger_obj_field(register_num, fc, buf, size) },
376        ) {
377            Result::Ok(buffer) => Result::Ok(buffer.map(|b| b.into())),
378            Result::Err(e) => Result::Err(e),
379        }
380    }
381}
382
383/// Implementation of `FieldGetter` for 256-bit cryptographic hashes.
384///
385/// This implementation handles 32-byte hash fields in XRPL ledger objects.
386/// Hash256 values are widely used throughout XRPL for transaction IDs,
387/// ledger indexes, object IDs, and various cryptographic operations.
388///
389/// # Buffer Management
390///
391/// Uses a 32-byte buffer (HASH256_SIZE) and validates that exactly 32 bytes
392/// are returned from the host function to ensure data integrity.
393impl FieldGetter for Hash256 {
394    #[inline]
395    fn get_from_current_ledger_obj(field_code: i32) -> Result<Self> {
396        match get_fixed_size_field_with_expected_bytes::<HASH256_SIZE, _>(
397            field_code,
398            |fc, buf, size| unsafe { get_current_ledger_obj_field(fc, buf, size) },
399        ) {
400            Result::Ok(buffer) => Result::Ok(buffer.into()),
401            Result::Err(e) => Result::Err(e),
402        }
403    }
404
405    #[inline]
406    fn get_from_current_ledger_obj_optional(field_code: i32) -> Result<Option<Self>> {
407        match get_fixed_size_field_with_expected_bytes_optional::<HASH256_SIZE, _>(
408            field_code,
409            |fc, buf, size| unsafe { get_current_ledger_obj_field(fc, buf, size) },
410        ) {
411            Result::Ok(buffer) => Result::Ok(buffer.map(|b| b.into())),
412            Result::Err(e) => Result::Err(e),
413        }
414    }
415
416    #[inline]
417    fn get_from_ledger_obj(register_num: i32, field_code: i32) -> Result<Self> {
418        match get_fixed_size_field_with_expected_bytes::<HASH256_SIZE, _>(
419            field_code,
420            |fc, buf, size| unsafe { get_ledger_obj_field(register_num, fc, buf, size) },
421        ) {
422            Result::Ok(buffer) => Result::Ok(buffer.into()),
423            Result::Err(e) => Result::Err(e),
424        }
425    }
426
427    #[inline]
428    fn get_from_ledger_obj_optional(register_num: i32, field_code: i32) -> Result<Option<Self>> {
429        match get_fixed_size_field_with_expected_bytes_optional::<HASH256_SIZE, _>(
430            field_code,
431            |fc, buf, size| unsafe { get_ledger_obj_field(register_num, fc, buf, size) },
432        ) {
433            Result::Ok(buffer) => Result::Ok(buffer.map(|b| b.into())),
434            Result::Err(e) => Result::Err(e),
435        }
436    }
437}
438
439/// Implementation of `FieldGetter` for variable-length binary data.
440///
441/// This implementation handles blob fields in XRPL ledger objects, which can contain
442/// arbitrary binary data such as memos, signatures, public keys, and other
443/// variable-length content.
444///
445/// # Buffer Management
446///
447/// Uses a buffer of size `N` to accommodate blob field data. The actual
448/// length of the data is determined by the return value from the host function
449/// and stored in the Blob's `len` field. No strict byte count validation is
450/// performed since blobs can vary significantly in size.
451///
452/// # Type Parameters
453///
454/// * `N` - The maximum capacity of the blob buffer in bytes
455impl<const N: usize> FieldGetter for Blob<N> {
456    #[inline]
457    fn get_from_current_ledger_obj(field_code: i32) -> Result<Self> {
458        match get_variable_size_field::<N, _>(field_code, |fc, buf, size| unsafe {
459            get_current_ledger_obj_field(fc, buf, size)
460        }) {
461            Result::Ok((data, len)) => Result::Ok(Blob { data, len }),
462            Result::Err(e) => Result::Err(e),
463        }
464    }
465
466    #[inline]
467    fn get_from_current_ledger_obj_optional(field_code: i32) -> Result<Option<Self>> {
468        match get_variable_size_field_optional::<N, _>(field_code, |fc, buf, size| unsafe {
469            get_current_ledger_obj_field(fc, buf, size)
470        }) {
471            Result::Ok(opt) => Result::Ok(opt.map(|(data, len)| Blob { data, len })),
472            Result::Err(e) => Result::Err(e),
473        }
474    }
475
476    #[inline]
477    fn get_from_ledger_obj(register_num: i32, field_code: i32) -> Result<Self> {
478        match get_variable_size_field::<N, _>(field_code, |fc, buf, size| unsafe {
479            get_ledger_obj_field(register_num, fc, buf, size)
480        }) {
481            Result::Ok((data, len)) => Result::Ok(Blob { data, len }),
482            Result::Err(e) => Result::Err(e),
483        }
484    }
485
486    #[inline]
487    fn get_from_ledger_obj_optional(register_num: i32, field_code: i32) -> Result<Option<Self>> {
488        match get_variable_size_field_optional::<N, _>(field_code, |fc, buf, size| unsafe {
489            get_ledger_obj_field(register_num, fc, buf, size)
490        }) {
491            Result::Ok(opt) => Result::Ok(opt.map(|(data, len)| Blob { data, len })),
492            Result::Err(e) => Result::Err(e),
493        }
494    }
495}
496
497/// Implementation of `FieldGetter` for XRPL currency codes.
498///
499/// This implementation handles 20-byte currency code fields in XRPL ledger objects.
500/// Currency codes uniquely identify different currencies and assets on the XRPL.
501///
502/// # Buffer Management
503///
504/// Uses a 20-byte buffer and validates that exactly 20 bytes are returned
505/// from the host function to ensure data integrity.
506impl FieldGetter for Currency {
507    #[inline]
508    fn get_from_current_ledger_obj(field_code: i32) -> Result<Self> {
509        match get_fixed_size_field_with_expected_bytes::<CURRENCY_SIZE, _>(
510            field_code,
511            |fc, buf, size| unsafe { get_current_ledger_obj_field(fc, buf, size) },
512        ) {
513            Result::Ok(buffer) => Result::Ok(buffer.into()),
514            Result::Err(e) => Result::Err(e),
515        }
516    }
517
518    #[inline]
519    fn get_from_current_ledger_obj_optional(field_code: i32) -> Result<Option<Self>> {
520        match get_fixed_size_field_with_expected_bytes_optional::<CURRENCY_SIZE, _>(
521            field_code,
522            |fc, buf, size| unsafe { get_current_ledger_obj_field(fc, buf, size) },
523        ) {
524            Result::Ok(buffer) => Result::Ok(buffer.map(|b| b.into())),
525            Result::Err(e) => Result::Err(e),
526        }
527    }
528
529    #[inline]
530    fn get_from_ledger_obj(register_num: i32, field_code: i32) -> Result<Self> {
531        match get_fixed_size_field_with_expected_bytes::<CURRENCY_SIZE, _>(
532            field_code,
533            |fc, buf, size| unsafe { get_ledger_obj_field(register_num, fc, buf, size) },
534        ) {
535            Result::Ok(buffer) => Result::Ok(buffer.into()),
536            Result::Err(e) => Result::Err(e),
537        }
538    }
539
540    #[inline]
541    fn get_from_ledger_obj_optional(register_num: i32, field_code: i32) -> Result<Option<Self>> {
542        match get_fixed_size_field_with_expected_bytes_optional::<CURRENCY_SIZE, _>(
543            field_code,
544            |fc, buf, size| unsafe { get_ledger_obj_field(register_num, fc, buf, size) },
545        ) {
546            Result::Ok(buffer) => Result::Ok(buffer.map(|b| b.into())),
547            Result::Err(e) => Result::Err(e),
548        }
549    }
550}
551
552/// Implementation of `FieldGetter` for XRPL issues.
553///
554/// This implementation handles issue fields in XRPL ledger objects.
555/// Supports all three Issue variants: XRP, IOU, and MPT.
556///
557/// # Buffer Management
558///
559/// Uses a 40-byte buffer to accommodate all Issue types:
560/// - XRP: 20 bytes (all zeros)
561/// - IOU: 40 bytes (20 bytes currency + 20 bytes issuer)
562/// - MPT: 24 bytes (4 bytes sequence + 20 bytes issuer)
563///
564/// The implementation detects the Issue type based on the number of bytes returned
565/// from the host function.
566impl FieldGetter for Issue {
567    #[inline]
568    fn get_from_current_ledger_obj(field_code: i32) -> Result<Self> {
569        match get_variable_size_field::<40, _>(field_code, |fc, buf, size| unsafe {
570            get_current_ledger_obj_field(fc, buf, size)
571        }) {
572            Result::Ok((buffer, len)) => Issue::from_buffer(buffer, len),
573            Result::Err(e) => Result::Err(e),
574        }
575    }
576
577    #[inline]
578    fn get_from_current_ledger_obj_optional(field_code: i32) -> Result<Option<Self>> {
579        match get_variable_size_field_optional::<40, _>(field_code, |fc, buf, size| unsafe {
580            get_current_ledger_obj_field(fc, buf, size)
581        }) {
582            Result::Ok(Some((buffer, len))) => match Issue::from_buffer(buffer, len) {
583                Result::Ok(issue) => Result::Ok(Some(issue)),
584                Result::Err(e) => Result::Err(e),
585            },
586            Result::Ok(None) => Result::Ok(None),
587            Result::Err(e) => Result::Err(e),
588        }
589    }
590
591    #[inline]
592    fn get_from_ledger_obj(register_num: i32, field_code: i32) -> Result<Self> {
593        match get_variable_size_field::<40, _>(field_code, |fc, buf, size| unsafe {
594            get_ledger_obj_field(register_num, fc, buf, size)
595        }) {
596            Result::Ok((buffer, len)) => Issue::from_buffer(buffer, len),
597            Result::Err(e) => Result::Err(e),
598        }
599    }
600
601    #[inline]
602    fn get_from_ledger_obj_optional(register_num: i32, field_code: i32) -> Result<Option<Self>> {
603        match get_variable_size_field_optional::<40, _>(field_code, |fc, buf, size| unsafe {
604            get_ledger_obj_field(register_num, fc, buf, size)
605        }) {
606            Result::Ok(Some((buffer, len))) => match Issue::from_buffer(buffer, len) {
607                Result::Ok(issue) => Result::Ok(Some(issue)),
608                Result::Err(e) => Result::Err(e),
609            },
610            Result::Ok(None) => Result::Ok(None),
611            Result::Err(e) => Result::Err(e),
612        }
613    }
614}
615
616pub mod current_ledger_object {
617    use super::FieldGetter;
618    use crate::host::Result;
619
620    /// Retrieves a field from the current ledger object.
621    ///
622    /// # Arguments
623    ///
624    /// * `field_code` - The field code identifying which field to retrieve
625    ///
626    /// # Returns
627    ///
628    /// Returns a `Result<T>` where:
629    /// * `Ok(T)` - The field value for the specified field
630    /// * `Err(Error)` - If the field cannot be retrieved or has unexpected size
631    #[inline]
632    pub fn get_field<T: FieldGetter>(field_code: i32) -> Result<T> {
633        T::get_from_current_ledger_obj(field_code)
634    }
635
636    /// Retrieves an optionally present field from the current ledger object.
637    ///
638    /// # Arguments
639    ///
640    /// * `field_code` - The field code identifying which field to retrieve
641    ///
642    /// # Returns
643    ///
644    /// Returns a `Result<Option<T>>` where:
645    /// * `Ok(Some(T))` - The field value for the specified field
646    /// * `Ok(None)` - If the field is not present
647    /// * `Err(Error)` - If the field cannot be retrieved or has unexpected size
648    #[inline]
649    pub fn get_field_optional<T: FieldGetter>(field_code: i32) -> Result<Option<T>> {
650        T::get_from_current_ledger_obj_optional(field_code)
651    }
652}
653
654pub mod ledger_object {
655    use super::FieldGetter;
656    use crate::host::Result;
657
658    /// Retrieves a field from a specified ledger object.
659    ///
660    /// # Arguments
661    ///
662    /// * `register_num` - The register number holding the ledger object to look for data in
663    /// * `field_code` - The field code identifying which field to retrieve
664    ///
665    /// # Returns
666    ///
667    /// Returns a `Result<T>` where:
668    /// * `Ok(T)` - The field value for the specified field
669    /// * `Err(Error)` - If the field cannot be retrieved or has unexpected size
670    #[inline]
671    pub fn get_field<T: FieldGetter>(register_num: i32, field_code: i32) -> Result<T> {
672        T::get_from_ledger_obj(register_num, field_code)
673    }
674
675    /// Retrieves an optionally present field from a specified ledger object.
676    ///
677    /// # Arguments
678    ///
679    /// * `register_num` - The register number holding the ledger object to look for data in
680    /// * `field_code` - The field code identifying which field to retrieve
681    ///
682    /// # Returns
683    ///
684    /// Returns a `Result<Option<T>>` where:
685    /// * `Ok(Some(T))` - The field value for the specified field
686    /// * `Ok(None)` - If the field is not present in the ledger object
687    /// * `Err(Error)` - If the field retrieval operation failed
688    #[inline]
689    pub fn get_field_optional<T: FieldGetter>(
690        register_num: i32,
691        field_code: i32,
692    ) -> Result<Option<T>> {
693        T::get_from_ledger_obj_optional(register_num, field_code)
694    }
695
696    #[cfg(test)]
697    mod tests {
698        use super::*;
699        use crate::core::ledger_objects::{current_ledger_object, ledger_object};
700        use crate::core::types::account_id::{ACCOUNT_ID_SIZE, AccountID};
701        use crate::core::types::amount::Amount;
702        use crate::core::types::blob::{Blob, DEFAULT_BLOB_SIZE};
703        use crate::core::types::public_key::PUBLIC_KEY_BUFFER_SIZE;
704        use crate::core::types::uint::{HASH128_SIZE, HASH256_SIZE, Hash128, Hash256};
705        use crate::sfield;
706
707        // ========================================
708        // Basic smoke tests for FieldGetter implementations
709        // These tests verify that the trait implementations compile and work with the test host.
710        // Note: The test host returns buffer_len as success, so these only verify basic functionality.
711        // ========================================
712
713        #[test]
714        fn test_field_getter_basic_types() {
715            // Test that all basic integer types work
716            assert!(u16::get_from_current_ledger_obj(sfield::LedgerEntryType).is_ok());
717            assert!(u32::get_from_current_ledger_obj(sfield::Flags).is_ok());
718            assert!(u64::get_from_current_ledger_obj(sfield::Balance).is_ok());
719        }
720
721        #[test]
722        fn test_field_getter_xrpl_types() {
723            // Test that XRPL-specific types work
724            assert!(AccountID::get_from_current_ledger_obj(sfield::Account).is_ok());
725            assert!(Amount::get_from_current_ledger_obj(sfield::Amount).is_ok());
726            assert!(Hash128::get_from_current_ledger_obj(sfield::EmailHash).is_ok());
727            assert!(Hash256::get_from_current_ledger_obj(sfield::PreviousTxnID).is_ok());
728
729            let blob: Blob<DEFAULT_BLOB_SIZE> =
730                Blob::get_from_current_ledger_obj(sfield::PublicKey).unwrap();
731            // The test host returns buffer length as the result
732            assert_eq!(blob.len, DEFAULT_BLOB_SIZE);
733        }
734
735        #[test]
736        fn test_field_getter_optional_variants() {
737            // Test optional field retrieval
738            let result = u32::get_from_current_ledger_obj_optional(sfield::Flags);
739            assert!(result.is_ok());
740            assert!(result.unwrap().is_some());
741
742            let result = AccountID::get_from_current_ledger_obj_optional(sfield::Account);
743            assert!(result.is_ok());
744            assert!(result.unwrap().is_some());
745        }
746
747        #[test]
748        fn test_field_getter_with_slot() {
749            // Test ledger object field retrieval with slot numbers
750            let slot = 0;
751            assert!(u32::get_from_ledger_obj(slot, sfield::Flags).is_ok());
752            assert!(u64::get_from_ledger_obj(slot, sfield::Balance).is_ok());
753            assert!(AccountID::get_from_ledger_obj(slot, sfield::Account).is_ok());
754        }
755
756        #[test]
757        fn test_field_getter_optional_with_slot() {
758            // Test optional field retrieval with slot numbers
759            let slot = 0;
760            let result = u32::get_from_ledger_obj_optional(slot, sfield::Flags);
761            assert!(result.is_ok());
762            assert!(result.unwrap().is_some());
763        }
764
765        // ========================================
766        // Tests for module-level convenience functions
767        // ========================================
768
769        #[test]
770        fn test_current_ledger_object_module() {
771            // Test the current_ledger_object module's convenience functions
772            assert!(current_ledger_object::get_field::<u32>(sfield::Flags).is_ok());
773            assert!(current_ledger_object::get_field::<AccountID>(sfield::Account).is_ok());
774
775            let result = current_ledger_object::get_field_optional::<u32>(sfield::Flags);
776            assert!(result.is_ok());
777            assert!(result.unwrap().is_some());
778        }
779
780        #[test]
781        fn test_ledger_object_module() {
782            // Test the ledger_object module's convenience functions
783            let slot = 0;
784            assert!(ledger_object::get_field::<u16>(slot, sfield::LedgerEntryType).is_ok());
785            assert!(ledger_object::get_field::<u32>(slot, sfield::Flags).is_ok());
786            assert!(ledger_object::get_field::<u64>(slot, sfield::Balance).is_ok());
787            assert!(ledger_object::get_field::<AccountID>(slot, sfield::Account).is_ok());
788            assert!(ledger_object::get_field::<Amount>(slot, sfield::Amount).is_ok());
789            assert!(ledger_object::get_field::<Hash128>(slot, sfield::EmailHash).is_ok());
790            assert!(ledger_object::get_field::<Hash256>(slot, sfield::PreviousTxnID).is_ok());
791            assert!(ledger_object::get_field::<Blob<33>>(slot, sfield::PublicKey).is_ok());
792
793            let result = ledger_object::get_field_optional::<u32>(slot, sfield::Flags);
794            assert!(result.is_ok());
795            assert!(result.unwrap().is_some());
796        }
797
798        // ========================================
799        // Type inference and compilation tests
800        // ========================================
801
802        #[test]
803        fn test_type_inference() {
804            let slot = 0;
805            // Verify type inference works with turbofish syntax
806            let _balance = get_field::<u64>(slot, sfield::Balance);
807            let _account = get_field::<AccountID>(slot, sfield::Account);
808
809            // Verify type inference works with type annotations
810            let _sequence: Result<u32> = get_field(slot, sfield::Sequence);
811            let _flags: Result<u32> = get_field(slot, sfield::Flags);
812        }
813
814        // ========================================
815        // Data size verification tests
816        // ========================================
817
818        #[test]
819        fn test_type_sizes() {
820            // Verify that returned types have the expected sizes
821            let hash128 = Hash128::get_from_current_ledger_obj(sfield::EmailHash).unwrap();
822            assert_eq!(hash128.as_bytes().len(), HASH128_SIZE);
823
824            let hash256 = Hash256::get_from_current_ledger_obj(sfield::PreviousTxnID).unwrap();
825            assert_eq!(hash256.as_bytes().len(), HASH256_SIZE);
826
827            let account = AccountID::get_from_current_ledger_obj(sfield::Account).unwrap();
828            assert_eq!(account.0.len(), ACCOUNT_ID_SIZE);
829
830            let blob: Blob<{ PUBLIC_KEY_BUFFER_SIZE }> =
831                Blob::get_from_current_ledger_obj(sfield::PublicKey).unwrap();
832            // In test environment, host returns buffer size as result code
833            assert_eq!(blob.len, PUBLIC_KEY_BUFFER_SIZE);
834            assert_eq!(blob.data.len(), PUBLIC_KEY_BUFFER_SIZE);
835        }
836    }
837}