xrpl_wasm_stdlib/host/
field_helpers.rs

1use crate::host::Result;
2use crate::host::error_codes::{
3    match_result_code, match_result_code_optional, match_result_code_with_expected_bytes,
4    match_result_code_with_expected_bytes_optional,
5};
6
7/// Helper function for retrieving fixed-size fields with exact byte validation.
8///
9/// This function encapsulates the common pattern of:
10/// 1. Allocating a buffer of fixed size
11/// 2. Calling a host function to retrieve the field
12/// 3. Validating that exactly the expected number of bytes were returned
13/// 4. Returning the initialized buffer
14///
15/// # Type Parameters
16///
17/// * `N` - The size of the buffer (compile-time constant)
18/// * `F` - The type of the host function closure
19///
20/// # Arguments
21///
22/// * `field_code` - The field code identifying which field to retrieve
23/// * `host_fn` - A closure that calls the appropriate host function
24///   - Takes: (field_code: i32, buffer_ptr: *mut u8, buffer_size: usize) -> i32
25///   - Returns: result code (number of bytes written or error code)
26///
27/// # Returns
28///
29/// Returns `Result<[u8; N]>` containing the initialized buffer if successful
30///
31/// # Example
32///
33/// ```ignore
34/// let buffer = get_fixed_size_field_with_expected_bytes::<20>(
35///     field_code,
36///     |fc, buf, size| unsafe { get_current_ledger_obj_field(fc, buf, size) },
37/// )?;
38/// ```
39#[inline]
40pub fn get_fixed_size_field_with_expected_bytes<const N: usize, F>(
41    field_code: i32,
42    host_fn: F,
43) -> Result<[u8; N]>
44where
45    F: FnOnce(i32, *mut u8, usize) -> i32,
46{
47    let mut buffer = core::mem::MaybeUninit::<[u8; N]>::uninit();
48    let result_code = host_fn(field_code, buffer.as_mut_ptr().cast(), N);
49    match_result_code_with_expected_bytes(result_code, N, || unsafe { buffer.assume_init() })
50}
51
52/// Optional variant of `get_fixed_size_field_with_expected_bytes`.
53///
54/// Returns `None` if the field is not found, otherwise behaves identically to the required variant.
55///
56/// # Type Parameters
57///
58/// * `N` - The size of the buffer (compile-time constant)
59/// * `F` - The type of the host function closure
60///
61/// # Arguments
62///
63/// * `field_code` - The field code identifying which field to retrieve
64/// * `host_fn` - A closure that calls the appropriate host function
65///
66/// # Returns
67///
68/// Returns `Result<Option<[u8; N]>>` where:
69/// * `Ok(Some(buffer))` - If the field is present and has the expected size
70/// * `Ok(None)` - If the field is not found
71/// * `Err(Error)` - If there's an error retrieving the field
72///
73/// # Example
74///
75/// ```ignore
76/// let buffer = get_fixed_size_field_with_expected_bytes_optional::<20>(
77///     field_code,
78///     |fc, buf, size| unsafe { get_current_ledger_obj_field(fc, buf, size) },
79/// )?;
80/// ```
81#[inline]
82pub fn get_fixed_size_field_with_expected_bytes_optional<const N: usize, F>(
83    field_code: i32,
84    host_fn: F,
85) -> Result<Option<[u8; N]>>
86where
87    F: FnOnce(i32, *mut u8, usize) -> i32,
88{
89    let mut buffer = core::mem::MaybeUninit::<[u8; N]>::uninit();
90    let result_code = host_fn(field_code, buffer.as_mut_ptr().cast(), N);
91    match_result_code_with_expected_bytes_optional(result_code, N, || {
92        Some(unsafe { buffer.assume_init() })
93    })
94}
95
96/// Helper function for retrieving variable-size fields.
97///
98/// This function encapsulates the common pattern for variable-size fields where:
99/// 1. A buffer of maximum size is allocated
100/// 2. A host function is called to retrieve the field
101/// 3. The actual number of bytes written is returned (not validated for exact match)
102/// 4. Both the buffer and the actual length are returned
103///
104/// This is used for fields like Amount and Blob where the actual size can vary.
105///
106/// # Type Parameters
107///
108/// * `N` - The maximum size of the buffer (compile-time constant)
109/// * `F` - The type of the host function closure
110///
111/// # Arguments
112///
113/// * `field_code` - The field code identifying which field to retrieve
114/// * `host_fn` - A closure that calls the appropriate host function
115///   - Takes: (field_code: i32, buffer_ptr: *mut u8, buffer_size: usize) -> i32
116///   - Returns: result code (number of bytes written or error code)
117///
118/// # Returns
119///
120/// Returns `Result<([u8; N], usize)>` containing the buffer and actual length if successful
121///
122/// # Example
123///
124/// ```ignore
125/// let (buffer, len) = get_variable_size_field::<48>(
126///     field_code,
127///     |fc, buf, size| unsafe { get_current_ledger_obj_field(fc, buf, size) },
128/// )?;
129/// ```
130#[inline]
131pub fn get_variable_size_field<const N: usize, F>(
132    field_code: i32,
133    host_fn: F,
134) -> Result<([u8; N], usize)>
135where
136    F: FnOnce(i32, *mut u8, usize) -> i32,
137{
138    let mut buffer = core::mem::MaybeUninit::<[u8; N]>::uninit();
139    let result_code = host_fn(field_code, buffer.as_mut_ptr().cast(), N);
140    match_result_code(result_code, || {
141        let len = result_code as usize;
142        (unsafe { buffer.assume_init() }, len)
143    })
144}
145
146/// Optional variant of `get_variable_size_field`.
147///
148/// Returns `None` if the field is not found, otherwise behaves identically to the required variant.
149///
150/// # Type Parameters
151///
152/// * `N` - The maximum size of the buffer (compile-time constant)
153/// * `F` - The type of the host function closure
154///
155/// # Arguments
156///
157/// * `field_code` - The field code identifying which field to retrieve
158/// * `host_fn` - A closure that calls the appropriate host function
159///
160/// # Returns
161///
162/// Returns `Result<Option<([u8; N], usize)>>` where:
163/// * `Ok(Some((buffer, len)))` - If the field is present
164/// * `Ok(None)` - If the field is not found
165/// * `Err(Error)` - If there's an error retrieving the field
166///
167/// # Example
168///
169/// ```ignore
170/// let result = get_variable_size_field_optional::<48>(
171///     field_code,
172///     |fc, buf, size| unsafe { get_current_ledger_obj_field(fc, buf, size) },
173/// )?;
174/// ```
175#[inline]
176pub fn get_variable_size_field_optional<const N: usize, F>(
177    field_code: i32,
178    host_fn: F,
179) -> Result<Option<([u8; N], usize)>>
180where
181    F: FnOnce(i32, *mut u8, usize) -> i32,
182{
183    let mut buffer = core::mem::MaybeUninit::<[u8; N]>::uninit();
184    let result_code = host_fn(field_code, buffer.as_mut_ptr().cast(), N);
185    match_result_code_optional(result_code, || {
186        let len = result_code as usize;
187        Some((unsafe { buffer.assume_init() }, len))
188    })
189}