libddwaf/object/
mod.rs

1#![doc = "Data model for exchanging data with the in-app WAF."]
2
3use std::alloc::Layout;
4use std::ops::{Deref, DerefMut, Index, IndexMut};
5use std::ptr::null_mut;
6use std::{cmp, fmt};
7
8mod iter;
9#[doc(inline)]
10pub use iter::*;
11
12/// Identifies the type of the value stored in a [`WafObject`].
13#[non_exhaustive]
14#[derive(Copy, Clone, Debug, PartialEq, Eq)]
15pub enum WafObjectType {
16    /// An invalid value. This can be used as a placeholder to retain the key
17    /// associated with an object that was only aprtially encoded.
18    Invalid,
19    /// A signed integer with 64-bit precision.
20    Signed,
21    /// An unsigned integer with 64-bit precision.
22    Unsigned,
23    /// A string value.
24    String,
25    /// An array of [`WafObject`]s.
26    Array,
27    /// A map of string-keyed [`WafObject`]s.
28    Map,
29    /// A boolean value.
30    Bool,
31    /// A floating point value (64-bit precision).
32    Float,
33    /// The null value.
34    Null,
35}
36impl WafObjectType {
37    /// Returns the raw [`libddwaf_sys::DDWAF_OBJ_TYPE`] value corresponding to this [`WafObjectType`].
38    const fn as_raw(self) -> libddwaf_sys::DDWAF_OBJ_TYPE {
39        match self {
40            WafObjectType::Invalid => libddwaf_sys::DDWAF_OBJ_INVALID,
41            WafObjectType::Signed => libddwaf_sys::DDWAF_OBJ_SIGNED,
42            WafObjectType::Unsigned => libddwaf_sys::DDWAF_OBJ_UNSIGNED,
43            WafObjectType::String => libddwaf_sys::DDWAF_OBJ_STRING,
44            WafObjectType::Array => libddwaf_sys::DDWAF_OBJ_ARRAY,
45            WafObjectType::Map => libddwaf_sys::DDWAF_OBJ_MAP,
46            WafObjectType::Bool => libddwaf_sys::DDWAF_OBJ_BOOL,
47            WafObjectType::Float => libddwaf_sys::DDWAF_OBJ_FLOAT,
48            WafObjectType::Null => libddwaf_sys::DDWAF_OBJ_NULL,
49        }
50    }
51}
52impl TryFrom<libddwaf_sys::DDWAF_OBJ_TYPE> for WafObjectType {
53    type Error = UnknownObjectTypeError;
54    fn try_from(value: libddwaf_sys::DDWAF_OBJ_TYPE) -> Result<Self, UnknownObjectTypeError> {
55        match value {
56            libddwaf_sys::DDWAF_OBJ_INVALID => Ok(WafObjectType::Invalid),
57            libddwaf_sys::DDWAF_OBJ_SIGNED => Ok(WafObjectType::Signed),
58            libddwaf_sys::DDWAF_OBJ_UNSIGNED => Ok(WafObjectType::Unsigned),
59            libddwaf_sys::DDWAF_OBJ_STRING => Ok(WafObjectType::String),
60            libddwaf_sys::DDWAF_OBJ_ARRAY => Ok(WafObjectType::Array),
61            libddwaf_sys::DDWAF_OBJ_MAP => Ok(WafObjectType::Map),
62            libddwaf_sys::DDWAF_OBJ_BOOL => Ok(WafObjectType::Bool),
63            libddwaf_sys::DDWAF_OBJ_FLOAT => Ok(WafObjectType::Float),
64            libddwaf_sys::DDWAF_OBJ_NULL => Ok(WafObjectType::Null),
65            unknown => Err(UnknownObjectTypeError(unknown)),
66        }
67    }
68}
69
70/// The error that is returned when a [`WafObject`] does not have a known, valid [`WafObjectType`].
71#[derive(Copy, Clone, Debug)]
72pub struct UnknownObjectTypeError(libddwaf_sys::DDWAF_OBJ_TYPE);
73impl std::error::Error for UnknownObjectTypeError {}
74impl std::fmt::Display for UnknownObjectTypeError {
75    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76        write!(f, "Unknown object type: {:?}", self.0)
77    }
78}
79
80/// The error that is returned when a [`WafObject`] does not have the expected [`WafObjectType`].
81#[derive(Copy, Clone, Debug)]
82pub struct ObjectTypeError {
83    pub expected: WafObjectType,
84    pub actual: WafObjectType,
85}
86impl std::error::Error for ObjectTypeError {}
87impl std::fmt::Display for ObjectTypeError {
88    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89        write!(
90            f,
91            "Invalid object type (expected {:?}, got {:?})",
92            self.expected, self.actual
93        )
94    }
95}
96
97/// This trait allow obtaining direct mutable access to the underlying memory
98/// backing a [`WafObject`] or [`TypedWafObject`] value.
99#[doc(hidden)]
100pub trait AsRawMutObject: crate::private::Sealed + AsRef<libddwaf_sys::ddwaf_object> {
101    /// Obtains a mutable reference to the underlying raw [`libddwaf_sys::ddwaf_object`].
102    ///
103    /// # Safety
104    /// The caller must ensure that:
105    /// - it does not change the [`libddwaf_sys::ddwaf_object::type_`] field,
106    /// - it does not change the pointers to values that don't outlive the [`libddwaf_sys::ddwaf_object`]
107    ///   itself, or whose memory cannot be recclaimed byt the destructor in the same way as the
108    ///   current value,
109    /// - it does not change the lengths in such a way that the object is no longer valid.
110    ///
111    /// Additionally, the caller would incur a memory leak if it dropped the value through the
112    /// returned reference (e.g, by calling [`std::mem::replace`]), since [`libddwaf_sys::ddwaf_object`] is
113    /// not [`Drop`] (see swapped destructors in
114    /// [this playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=aeea4aba8f960bf0c63f6185f016a94d).
115    #[doc(hidden)]
116    unsafe fn as_raw_mut(&mut self) -> &mut libddwaf_sys::ddwaf_object;
117}
118
119/// This trait is implemented by type-safe interfaces to the [`WafObject`], with
120/// one implementation for each [`WafObjectType`].
121pub trait TypedWafObject: AsRawMutObject {
122    /// The associated [`WafObjectType`] constant corresponding to the typed
123    /// object's type discriminator.
124    const TYPE: WafObjectType;
125}
126
127/// The low-level representation of an arbitrary WAF object.
128///
129/// It is usually converted to a [`TypedWafObject`] by calling [`WafObject::as_type`].
130#[derive(Default)]
131#[repr(transparent)]
132pub struct WafObject {
133    raw: libddwaf_sys::ddwaf_object,
134}
135impl WafObject {
136    /// Creates a new [`WafObject`] from a JSON string.
137    ///
138    /// This function is not intended to be used with un-trusted/adversarial
139    /// input. The typical use-case is to facilitate parsing rulesets for use
140    /// with [`crate::builder::Builder::add_or_update_config`].
141    ///
142    /// # Returns
143    /// Returns [`None`] if parsing the JSON string into a [`WafObject`] was not
144    /// possible, or if the input JSON string is larger than [`u32::MAX`] bytes.
145    pub fn from_json(json: impl AsRef<[u8]>) -> Option<WafOwned<Self>> {
146        let mut obj = WafOwned::<Self>::default();
147        let data = json.as_ref();
148        let Ok(len) = u32::try_from(data.len()) else {
149            return None;
150        };
151        if !unsafe {
152            libddwaf_sys::ddwaf_object_from_json(obj.as_raw_mut(), data.as_ptr().cast(), len)
153        } {
154            return None;
155        }
156        Some(obj)
157    }
158
159    /// Returns the [`WafObjectType`] of the underlying value.
160    ///
161    /// Returns [`WafObjectType::Invalid`] if the underlying value's type is not set to a
162    /// known, valid [`WafObjectType`] value.
163    #[must_use]
164    pub fn get_type(&self) -> WafObjectType {
165        self.as_ref()
166            .type_
167            .try_into()
168            .unwrap_or(WafObjectType::Invalid)
169    }
170
171    /// Returns a reference to this value as a `T` if its type corresponds.
172    #[must_use]
173    pub fn as_type<T: TypedWafObject>(&self) -> Option<&T> {
174        if self.get_type() == T::TYPE {
175            Some(unsafe { self.as_type_unchecked::<T>() })
176        } else {
177            None
178        }
179    }
180
181    /// Returns a reference to this value as a `T`.
182    ///
183    /// # Safety
184    /// The caller must ensure that the [`WafObject`] can be accurately represented by `T`.
185    pub(crate) unsafe fn as_type_unchecked<T: TypedWafObject>(&self) -> &T {
186        unsafe { self.as_ref().unchecked_as_ref::<T>() }
187    }
188
189    /// Returns a mutable reference to this value as a `T` if its type corresponds.
190    pub fn as_type_mut<T: TypedWafObject>(&mut self) -> Option<&mut T> {
191        if self.get_type() == T::TYPE {
192            Some(unsafe { self.as_raw_mut().unchecked_as_ref_mut::<T>() })
193        } else {
194            None
195        }
196    }
197
198    /// Returns true if this [`WafObject`] is not [`WafObjectType::Invalid`], meaning it can be
199    /// converted to one of the [`TypedWafObject`] implementations.
200    #[must_use]
201    pub fn is_valid(&self) -> bool {
202        self.get_type() != WafObjectType::Invalid
203    }
204
205    /// Returns the value of this [`WafObject`] as a [`u64`] if its type is [`WafObjectType::Unsigned`].
206    #[must_use]
207    pub fn to_u64(&self) -> Option<u64> {
208        self.as_type::<WafUnsigned>().map(WafUnsigned::value)
209    }
210
211    /// Returns the value of this [`WafObject`] as a [`i64`] if its type is [`WafObjectType::Signed`] (or
212    /// [`WafObjectType::Unsigned`] with a value that can be represented as an [`i64`]).
213    #[must_use]
214    pub fn to_i64(&self) -> Option<i64> {
215        match self.get_type() {
216            WafObjectType::Unsigned => {
217                let obj: &WafUnsigned = unsafe { self.as_type_unchecked() };
218                obj.value().try_into().ok()
219            }
220            WafObjectType::Signed => {
221                let obj: &WafSigned = unsafe { self.as_type_unchecked() };
222                Some(obj.value())
223            }
224            _ => None,
225        }
226    }
227
228    /// Returns the value of this [`WafObject`] as a [`f64`] if its type is [`WafObjectType::Float`].
229    #[must_use]
230    pub fn to_f64(&self) -> Option<f64> {
231        self.as_type::<WafFloat>().map(WafFloat::value)
232    }
233
234    /// Returns the value of this [`WafObject`] as a [`bool`] if its type is [`WafObjectType::Bool`].
235    #[must_use]
236    pub fn to_bool(&self) -> Option<bool> {
237        self.as_type::<WafBool>().map(WafBool::value)
238    }
239
240    /// Returns the value of this [`WafObject`] as a [`&str`] if its type is [`WafObjectType::String`],
241    /// and the value is valid UTF-8.
242    #[must_use]
243    pub fn to_str(&self) -> Option<&str> {
244        self.as_type::<WafString>().and_then(|x| x.as_str().ok())
245    }
246}
247impl AsRef<libddwaf_sys::ddwaf_object> for WafObject {
248    fn as_ref(&self) -> &libddwaf_sys::ddwaf_object {
249        &self.raw
250    }
251}
252impl AsRawMutObject for WafObject {
253    unsafe fn as_raw_mut(&mut self) -> &mut libddwaf_sys::ddwaf_object {
254        &mut self.raw
255    }
256}
257impl fmt::Debug for WafObject {
258    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
259        match self.get_type() {
260            WafObjectType::Invalid => write!(f, "WafInvalid"),
261            WafObjectType::Unsigned => {
262                let obj: &WafUnsigned = self.as_type().unwrap();
263                obj.fmt(f)
264            }
265            WafObjectType::Signed => {
266                let obj: &WafSigned = self.as_type().unwrap();
267                obj.fmt(f)
268            }
269            WafObjectType::Float => {
270                let obj: &WafFloat = self.as_type().unwrap();
271                obj.fmt(f)
272            }
273            WafObjectType::Bool => {
274                let obj: &WafBool = self.as_type().unwrap();
275                obj.fmt(f)
276            }
277            WafObjectType::Null => {
278                let obj: &WafNull = self.as_type().unwrap();
279                obj.fmt(f)
280            }
281            WafObjectType::String => {
282                let obj: &WafString = self.as_type().unwrap();
283                obj.fmt(f)
284            }
285            WafObjectType::Array => {
286                let obj: &WafArray = self.as_type().unwrap();
287                obj.fmt(f)
288            }
289            WafObjectType::Map => {
290                let obj: &WafMap = self.as_type().unwrap();
291                obj.fmt(f)
292            }
293        }
294    }
295}
296impl Drop for WafObject {
297    fn drop(&mut self) {
298        unsafe { self.raw.drop_object() }
299    }
300}
301impl From<u64> for WafObject {
302    fn from(value: u64) -> Self {
303        WafUnsigned::new(value).into()
304    }
305}
306impl From<u32> for WafObject {
307    fn from(value: u32) -> Self {
308        WafUnsigned::new(value.into()).into()
309    }
310}
311impl From<i64> for WafObject {
312    fn from(value: i64) -> Self {
313        WafSigned::new(value).into()
314    }
315}
316impl From<i32> for WafObject {
317    fn from(value: i32) -> Self {
318        WafSigned::new(value.into()).into()
319    }
320}
321impl From<f64> for WafObject {
322    fn from(value: f64) -> Self {
323        WafFloat::new(value).into()
324    }
325}
326impl From<bool> for WafObject {
327    fn from(value: bool) -> Self {
328        WafBool::new(value).into()
329    }
330}
331impl From<&str> for WafObject {
332    fn from(value: &str) -> Self {
333        WafString::new(value).into()
334    }
335}
336impl From<&[u8]> for WafObject {
337    fn from(value: &[u8]) -> Self {
338        WafString::new(value).into()
339    }
340}
341impl From<()> for WafObject {
342    fn from((): ()) -> Self {
343        WafNull::new().into()
344    }
345}
346impl<T: TypedWafObject> From<T> for WafObject {
347    fn from(value: T) -> Self {
348        let res = Self {
349            raw: *value.as_ref(),
350        };
351        std::mem::forget(value);
352        res
353    }
354}
355impl<T: AsRef<libddwaf_sys::ddwaf_object>> cmp::PartialEq<T> for WafObject {
356    fn eq(&self, other: &T) -> bool {
357        self.raw == *other.as_ref()
358    }
359}
360impl crate::private::Sealed for WafObject {}
361
362/// A WAF-owned [`WafObject`] or [`TypedWafObject`] value.
363///
364/// This has different [`Drop`] behavior than a rust-owned [`WafObject`] value.
365pub struct WafOwned<T: AsRawMutObject> {
366    inner: std::mem::ManuallyDrop<T>,
367}
368impl<T: AsRawMutObject + fmt::Debug> fmt::Debug for WafOwned<T> {
369    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
370        self.inner.deref().fmt(f)
371    }
372}
373impl<T: AsRawMutObject + Default> Default for WafOwned<T> {
374    fn default() -> Self {
375        Self {
376            inner: std::mem::ManuallyDrop::new(Default::default()),
377        }
378    }
379}
380impl<T: AsRawMutObject> Deref for WafOwned<T> {
381    type Target = T;
382    fn deref(&self) -> &Self::Target {
383        &self.inner
384    }
385}
386impl<T: AsRawMutObject> DerefMut for WafOwned<T> {
387    fn deref_mut(&mut self) -> &mut Self::Target {
388        &mut self.inner
389    }
390}
391impl<T: AsRawMutObject> Drop for WafOwned<T> {
392    fn drop(&mut self) {
393        unsafe { libddwaf_sys::ddwaf_object_free(self.inner.as_raw_mut()) };
394    }
395}
396impl<T: AsRawMutObject> PartialEq<T> for WafOwned<T>
397where
398    T: PartialEq<T>,
399{
400    fn eq(&self, other: &T) -> bool {
401        *self.inner == *other
402    }
403}
404
405/// Allocates memory for the given [`Layout`], calling [`std::alloc::handle_alloc_error`] if the
406/// allocation failed.
407///
408/// # Safety
409/// The requirements as for [`std::alloc::alloc`] apply.
410unsafe fn no_fail_alloc(layout: Layout) -> *mut u8 {
411    if layout.size() == 0 {
412        return null_mut();
413    }
414    let ptr = unsafe { std::alloc::alloc(layout) };
415    if ptr.is_null() {
416        std::alloc::handle_alloc_error(layout);
417    }
418    ptr
419}
420
421macro_rules! typed_object {
422    ($type:expr => $name:ident $({ $($impl:tt)* })?) => {
423        #[doc = concat!("The WAF object representation of a value of type [", stringify!($type), "]")]
424        #[repr(transparent)]
425        pub struct $name {
426            raw: libddwaf_sys::ddwaf_object,
427        }
428        impl $name {
429            #[doc = concat!("Returns true if this [", stringify!($name), "] is indeed [", stringify!($type), "].")]
430            #[must_use]
431            pub const fn is_valid(&self) -> bool {
432                self.raw.type_ == $type.as_raw()
433            }
434
435            /// Returns a reference to this value as a [`WafObject`].
436            #[must_use]
437            pub fn as_object(&self) -> &WafObject{
438                let obj: &libddwaf_sys::ddwaf_object = self.as_ref();
439                obj.as_object_ref()
440            }
441            $(
442            $($impl)*)?
443        }
444        impl AsRef<libddwaf_sys::ddwaf_object> for $name {
445            fn as_ref(&self) -> &libddwaf_sys::ddwaf_object {
446                &self.raw
447            }
448        }
449        impl AsRawMutObject for $name {
450            unsafe fn as_raw_mut(&mut self) -> &mut libddwaf_sys::ddwaf_object {
451                &mut self.raw
452            }
453        }
454        impl Default for $name {
455            fn default() -> Self {
456                Self {
457                    raw: libddwaf_sys::ddwaf_object {
458                        type_: $type.as_raw(),
459                        ..Default::default()
460                    },
461                }
462            }
463        }
464        impl TryFrom<WafObject> for $name {
465            type Error = ObjectTypeError;
466            fn try_from(obj: WafObject) -> Result<Self, Self::Error> {
467                if obj.get_type() != Self::TYPE {
468                    return Err(ObjectTypeError {
469                        expected: $type,
470                        actual: obj.get_type(),
471                    });
472                }
473                let res = Self { raw: obj.raw };
474                std::mem::forget(obj);
475                Ok(res)
476            }
477        }
478        impl<T: AsRef<libddwaf_sys::ddwaf_object>> cmp::PartialEq<T> for $name {
479            fn eq(&self, other: &T) -> bool {
480                self.raw == *other.as_ref()
481            }
482        }
483        impl crate::private::Sealed for $name {}
484        impl TypedWafObject for $name {
485            const TYPE: WafObjectType = $type;
486        }
487    };
488}
489
490typed_object!(WafObjectType::Invalid => WafInvalid);
491typed_object!(WafObjectType::Signed => WafSigned {
492    /// Creates a new [`WafSigned`] with the provided value.
493    #[must_use]
494    pub const fn new(val: i64) -> Self {
495        Self {
496            raw: libddwaf_sys::ddwaf_object {
497                type_: libddwaf_sys::DDWAF_OBJ_SIGNED,
498                #[allow(clippy::used_underscore_items)]
499                __bindgen_anon_1: libddwaf_sys::_ddwaf_object__bindgen_ty_1 { intValue: val },
500                nbEntries: 0,
501                parameterName: null_mut(),
502                parameterNameLength: 0,
503            }
504        }
505    }
506
507    /// Returns the value of this [`WafSigned`].
508    #[must_use]
509    pub const fn value(&self) -> i64 {
510        unsafe { self.raw.__bindgen_anon_1.intValue }
511    }
512});
513typed_object!(WafObjectType::Unsigned => WafUnsigned {
514    /// Creates a new [`WafUnsigned`] with the provided value.
515    #[must_use]
516    pub const fn new (val: u64) -> Self {
517        Self {
518            raw: libddwaf_sys::ddwaf_object {
519                type_: libddwaf_sys::DDWAF_OBJ_UNSIGNED,
520                #[allow(clippy::used_underscore_items)]
521                __bindgen_anon_1: libddwaf_sys::_ddwaf_object__bindgen_ty_1 { uintValue: val },
522                nbEntries: 0,
523                parameterName: null_mut(),
524                parameterNameLength: 0,
525            }
526        }
527    }
528
529    /// Retuns the value of this [`WafUnsigned`].
530    #[must_use]
531    pub const fn value(&self) -> u64 {
532        unsafe { self.raw.__bindgen_anon_1.uintValue }
533    }
534});
535typed_object!(WafObjectType::String => WafString {
536    /// Creates a new [`WafString`] with the provided value.
537    pub fn new(val: impl AsRef<[u8]>) -> Self {
538        let val = val.as_ref();
539        let ptr = if val.is_empty() {
540            null_mut()
541        } else {
542            let b: Box<[u8]> = val.into();
543            Box::into_raw(b).cast()
544        };
545        Self {
546            raw: libddwaf_sys::ddwaf_object {
547                type_: libddwaf_sys::DDWAF_OBJ_STRING,
548                #[allow(clippy::used_underscore_items)]
549                __bindgen_anon_1: libddwaf_sys::_ddwaf_object__bindgen_ty_1 {
550                    stringValue: ptr,
551                },
552                nbEntries: val.len() as u64,
553                ..Default::default()
554            },
555        }
556    }
557
558    /// Returns the length of this [`WafString`], in bytes.
559    ///
560    /// # Panics
561    /// Panics if the string is larger than [`usize::MAX`] bytes. This can only happen on
562    /// platforms where [`usize`] is 32-bit wide.
563    #[must_use]
564    pub fn len(&self) -> usize {
565        usize::try_from(self.raw.nbEntries).expect("string is too large for this platform")
566    }
567
568    /// Returns true if this [`WafString`] is empty.
569    #[must_use]
570    pub fn is_empty(&self) -> bool {
571        self.len() == 0
572    }
573
574    /// Returns a slice of the bytes from this [`WafString`].
575    #[must_use]
576    pub fn bytes(&self) -> &[u8] {
577        debug_assert_eq!(self.raw.type_, libddwaf_sys::DDWAF_OBJ_STRING);
578        let len = self.len();
579        if len == 0 {
580            return &[];
581        }
582        debug_assert!(!unsafe{ self.raw.__bindgen_anon_1.stringValue }.is_null());
583        unsafe {
584            std::slice::from_raw_parts(
585                self.raw.__bindgen_anon_1.stringValue.cast(),
586                len,
587            )
588        }
589    }
590
591    /// Returns a string slice from this [`WafString`].
592    ///
593    /// # Errors
594    /// Returns an error if the underlying data is not a valid UTF-8 string, under the same conditions as
595    /// [`std::str::from_utf8`].
596    pub fn as_str(&self) -> Result<&str, std::str::Utf8Error> {
597        std::str::from_utf8(self.bytes())
598    }
599});
600typed_object!(WafObjectType::Array => WafArray {
601    /// Creates a new [`WafArray`] with the provided size. All values in the array are initialized
602    /// to an invalid [`WafObject`] instance.
603    ///
604    /// # Panics
605    /// Panics when the provided `nb_entries` is larger than what the current platform can represent in an [`usize`].
606    /// This can only happen on platforms where [`usize`] is 32-bit wide.
607    #[must_use]
608    pub fn new(nb_entries: u64) -> Self {
609        let size = usize::try_from(nb_entries).expect("size is too large for this platform");
610        let layout = Layout::array::<libddwaf_sys::ddwaf_object>(size).unwrap();
611        let array = unsafe { no_fail_alloc(layout).cast() };
612        unsafe { std::ptr::write_bytes(array, 0, size)};
613        Self {
614            raw: libddwaf_sys::ddwaf_object {
615                type_: libddwaf_sys::DDWAF_OBJ_ARRAY,
616                #[allow(clippy::used_underscore_items)]
617                __bindgen_anon_1: libddwaf_sys::_ddwaf_object__bindgen_ty_1 { array },
618                nbEntries: nb_entries,
619                ..Default::default()
620            }
621        }
622    }
623
624    /// Returns the length of this [`WafArray`].
625    ///
626    /// # Panics
627    /// Panics if the array is larger than [`usize::MAX`] elements. This can only happen on
628    /// platforms where [`usize`] is 32-bit wide.
629    #[must_use]
630    pub fn len(&self) -> usize {
631        usize::try_from(self.raw.nbEntries).expect("array is too large for this platform")
632    }
633
634    /// Returns true if this [`WafArray`] is empty.
635    #[must_use]
636    pub fn is_empty(&self) -> bool {
637        self.len() == 0
638    }
639
640    /// Returns an iterator over the [`Keyed<WafObject>`]s in this [`WafMap`].
641    pub fn iter(&self) -> impl Iterator<Item = &WafObject> {
642        let slice : &[WafObject] = self.as_ref();
643        slice.iter()
644    }
645
646    /// Returns a mutable iterator over the [`Keyed<WafObject>`]s in this [`WafMap`].
647    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut WafObject> {
648        let slice : &mut [WafObject] = AsMut::as_mut(self);
649        slice.iter_mut()
650    }
651});
652typed_object!(WafObjectType::Map => WafMap {
653    /// Creates a new [`WafMap`] with the provided size. All values in the map are initialized
654    /// to an invalid [`WafObject`] instance with a blank key.
655    ///
656    /// # Panics
657    /// Panics when the provided `nbEntries` is larger than what the current platform can represent in an [`usize`].
658    /// This can only happen on platforms where [`usize`] is 32-bit wide.
659    #[must_use]
660    pub fn new(nb_entries: u64) -> Self {
661        let size = usize::try_from(nb_entries).expect("size is too large for this platform");
662        let layout = Layout::array::<libddwaf_sys::ddwaf_object>(size).unwrap();
663        let array = unsafe { no_fail_alloc(layout).cast() };
664        unsafe { std::ptr::write_bytes(array, 0, size)};
665        Self {
666            raw: libddwaf_sys::ddwaf_object {
667                type_: libddwaf_sys::DDWAF_OBJ_MAP,
668                #[allow(clippy::used_underscore_items)]
669                __bindgen_anon_1: libddwaf_sys::_ddwaf_object__bindgen_ty_1 {  array },
670                nbEntries: nb_entries,
671                ..Default::default()
672            }
673        }
674    }
675
676    /// Returns the length of this [`WafMap`].
677    ///
678    /// # Panics
679    /// Panics if the map is larger than [`usize::MAX`] elements. This can only happen on platforms
680    /// where [`usize`] is 32-bit wide.
681    #[must_use]
682    pub fn len(&self) -> usize {
683        usize::try_from(self.raw.nbEntries).expect("map is too large for this platform")
684    }
685
686    /// Returns true if this [`WafMap`] is empty.
687    #[must_use]
688    pub fn is_empty(&self) -> bool {
689        self.len() == 0
690    }
691
692    /// Returns an iterator over the [`Keyed<WafObject>`]s in this [`WafMap`].
693    pub fn iter(&self) -> impl Iterator<Item = &Keyed<WafObject>> {
694        let slice : &[Keyed<WafObject>] = self.as_ref();
695        slice.iter()
696    }
697
698    /// Returns a mutable iterator over the [`Keyed<WafObject>`]s in this [`WafMap`].
699    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Keyed<WafObject>> {
700        let slice : &mut [Keyed<WafObject>] = AsMut::as_mut(self);
701        slice.iter_mut()
702    }
703
704    /// Returns a reference to the [`Keyed<WafObject>`] with the provided key, if one exists.
705    ///
706    /// If multiple such objects exist in the receiver, the first match is returned.
707    #[must_use]
708    pub fn get(&self, key: &'_ [u8]) -> Option<&Keyed<WafObject>> {
709        for o in self.iter() {
710            if o.key() == key {
711                return Some(o)
712            }
713        }
714        None
715    }
716
717    /// Returns a mutable reference to the [`Keyed<WafObject>`] with the provided key, if one exists.
718    ///
719    /// If multiple such objects exist in the receiver, the first match is returned.
720    pub fn get_mut(&mut self, key: &'_ [u8]) -> Option<&mut Keyed<WafObject>> {
721        for o in self.iter_mut() {
722            if o.key() == key {
723                return Some(o)
724            }
725        }
726        None
727    }
728
729    /// Returns a reference to the [`Keyed<WafObject>`] with the provided key, if one exists.
730    #[must_use]
731    pub fn get_str(&self, key: &'_ str) -> Option<&Keyed<WafObject>> {
732        self.get(key.as_bytes())
733    }
734
735    /// Returns a mutable reference to the [`Keyed<WafObject>`] with the provided key, if one exists.
736    pub fn get_str_mut(&mut self, key: &'_ str) -> Option<&mut Keyed<WafObject>> {
737        self.get_mut(key.as_bytes())
738    }
739});
740typed_object!(WafObjectType::Bool => WafBool {
741    /// Creates a new [`WafBool`] with the provided value.
742    #[must_use]
743    pub const fn new(val: bool) -> Self {
744        Self {
745            raw: libddwaf_sys::ddwaf_object {
746                type_: libddwaf_sys::DDWAF_OBJ_BOOL,
747                #[allow(clippy::used_underscore_items)]
748                __bindgen_anon_1: libddwaf_sys::_ddwaf_object__bindgen_ty_1 { boolean: val },
749                nbEntries: 0,
750                parameterName: null_mut(),
751                parameterNameLength: 0,
752            }
753        }
754    }
755
756    /// Returns the value of this [`WafBool`].
757    #[must_use]
758    pub const fn value(&self) -> bool {
759        unsafe { self.raw.__bindgen_anon_1.boolean }
760    }
761});
762typed_object!(WafObjectType::Float => WafFloat {
763    /// Creates a new [`WafFloat`] with the provided value.
764    #[must_use]
765    pub const fn new(val: f64) -> Self {
766        Self {
767            raw: libddwaf_sys::ddwaf_object {
768                type_: libddwaf_sys::DDWAF_OBJ_FLOAT,
769                #[allow(clippy::used_underscore_items)]
770                __bindgen_anon_1: libddwaf_sys::_ddwaf_object__bindgen_ty_1 { f64_: val },
771                nbEntries: 0,
772                parameterName: null_mut(),
773                parameterNameLength: 0,
774            }
775        }
776    }
777
778    /// Returns the value of this [`WafFloat`].
779    #[must_use]
780    pub const fn value(&self) -> f64 {
781        unsafe { self.raw.__bindgen_anon_1.f64_ }
782    }
783});
784typed_object!(WafObjectType::Null => WafNull {
785    /// Creates a new [`WafNull`].
786    #[must_use]
787    pub const fn new() -> Self {
788        Self { raw: libddwaf_sys::ddwaf_object {
789            type_: libddwaf_sys::DDWAF_OBJ_NULL,
790            #[allow(clippy::used_underscore_items)]
791            __bindgen_anon_1: libddwaf_sys::_ddwaf_object__bindgen_ty_1 { uintValue: 0},
792            nbEntries: 0,
793            parameterName: null_mut(),
794            parameterNameLength: 0,
795        } }
796    }
797});
798
799impl fmt::Debug for WafSigned {
800    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
801        write!(f, "{}({})", stringify!(WafSigned), self.value())
802    }
803}
804impl From<i64> for WafSigned {
805    fn from(value: i64) -> Self {
806        Self::new(value)
807    }
808}
809impl From<i32> for WafSigned {
810    fn from(value: i32) -> Self {
811        Self::new(value.into())
812    }
813}
814
815impl fmt::Debug for WafUnsigned {
816    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
817        write!(f, "{}({})", stringify!(WafUnsigned), self.value())
818    }
819}
820impl From<u64> for WafUnsigned {
821    fn from(value: u64) -> Self {
822        Self::new(value)
823    }
824}
825impl From<u32> for WafUnsigned {
826    fn from(value: u32) -> Self {
827        Self::new(value.into())
828    }
829}
830
831impl<T: AsRef<[u8]>> From<T> for WafString {
832    fn from(val: T) -> Self {
833        Self::new(val)
834    }
835}
836impl fmt::Debug for WafString {
837    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
838        write!(
839            f,
840            "{}(\"{:?}\")",
841            stringify!(WafString),
842            fmt_bin_str(self.bytes())
843        )
844    }
845}
846impl Drop for WafString {
847    fn drop(&mut self) {
848        unsafe { self.raw.drop_string() }
849    }
850}
851
852impl AsRef<[WafObject]> for WafArray {
853    fn as_ref(&self) -> &[WafObject] {
854        if self.is_empty() {
855            return &[];
856        }
857        let array = unsafe { self.raw.__bindgen_anon_1.array.cast() };
858        unsafe { std::slice::from_raw_parts(array, self.len()) }
859    }
860}
861impl AsMut<[WafObject]> for WafArray {
862    fn as_mut(&mut self) -> &mut [WafObject] {
863        if self.is_empty() {
864            return &mut [];
865        }
866        let array = unsafe { self.raw.__bindgen_anon_1.array.cast() };
867        unsafe { std::slice::from_raw_parts_mut(array, self.len()) }
868    }
869}
870impl fmt::Debug for WafArray {
871    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
872        write!(f, "{}[", stringify!(WafArray))?;
873        let mut first = true;
874        for obj in self.iter() {
875            if first {
876                first = false;
877            } else {
878                write!(f, ", ")?;
879            }
880            write!(f, "{obj:?}")?;
881        }
882        write!(f, "]")
883    }
884}
885impl Drop for WafArray {
886    fn drop(&mut self) {
887        unsafe { self.raw.drop_array() }
888    }
889}
890impl<T: Into<WafObject>, const N: usize> From<[T; N]> for WafArray {
891    fn from(value: [T; N]) -> Self {
892        let mut array = Self::new(value.len() as u64);
893        for (i, obj) in value.into_iter().enumerate() {
894            array[i] = obj.into();
895        }
896        array
897    }
898}
899impl Index<usize> for WafArray {
900    type Output = WafObject;
901    fn index(&self, index: usize) -> &Self::Output {
902        let obj: &libddwaf_sys::ddwaf_object = self.as_ref();
903        assert!(
904            // The object might be larger than [usize::MAX], but we ignore this for simplicity's sake.
905            index < usize::try_from(obj.nbEntries).unwrap_or(usize::MAX),
906            "index out of bounds ({} >= {})",
907            index,
908            obj.nbEntries
909        );
910        let array = unsafe { obj.__bindgen_anon_1.array };
911        unsafe { &*(array.add(index) as *const _) }
912    }
913}
914impl IndexMut<usize> for WafArray {
915    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
916        let obj: &libddwaf_sys::ddwaf_object = self.as_ref();
917        assert!(
918            // The object might be larger than [usize::MAX], but we ignore this for simplicity's sake.
919            index < usize::try_from(obj.nbEntries).unwrap_or(usize::MAX),
920            "index out of bounds ({} >= {})",
921            index,
922            obj.nbEntries
923        );
924        let array = unsafe { obj.__bindgen_anon_1.array };
925        unsafe { &mut *(array.add(index).cast()) }
926    }
927}
928
929impl AsRef<[Keyed<WafObject>]> for WafMap {
930    fn as_ref(&self) -> &[Keyed<WafObject>] {
931        if self.is_empty() {
932            return &[];
933        }
934        let array = unsafe { self.raw.__bindgen_anon_1.array as *const _ };
935        unsafe { std::slice::from_raw_parts(array, self.len()) }
936    }
937}
938impl AsMut<[Keyed<WafObject>]> for WafMap {
939    fn as_mut(&mut self) -> &mut [Keyed<WafObject>] {
940        if self.is_empty() {
941            return &mut [];
942        }
943        let array = unsafe { self.raw.__bindgen_anon_1.array.cast() };
944        unsafe { std::slice::from_raw_parts_mut(array, self.len()) }
945    }
946}
947impl fmt::Debug for WafMap {
948    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
949        write!(f, "{}{{", stringify!(WafMap))?;
950        let mut first = true;
951        for keyed_obj in self.iter() {
952            if first {
953                first = false;
954            } else {
955                write!(f, ", ")?;
956            }
957            write!(f, "{keyed_obj:?}")?;
958        }
959        write!(f, "}}")
960    }
961}
962impl Drop for WafMap {
963    fn drop(&mut self) {
964        unsafe { self.raw.drop_map() }
965    }
966}
967impl Index<usize> for WafMap {
968    type Output = Keyed<WafObject>;
969    fn index(&self, index: usize) -> &Self::Output {
970        let obj: &libddwaf_sys::ddwaf_object = self.as_ref();
971        assert!(
972            // The object might be larger than [usize::MAX], but we ignore this for simplicity's sake.
973            index < usize::try_from(obj.nbEntries).unwrap_or(usize::MAX),
974            "index out of bounds ({} >= {})",
975            index,
976            obj.nbEntries
977        );
978        let array = unsafe { obj.__bindgen_anon_1.array };
979        unsafe { &*array.add(index) }.as_keyed_object_ref()
980    }
981}
982impl IndexMut<usize> for WafMap {
983    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
984        let obj: &libddwaf_sys::ddwaf_object = self.as_ref();
985        assert!(
986            // The object might be larger than [usize::MAX], but we ignore this for simplicity's sake.
987            index < usize::try_from(obj.nbEntries).unwrap_or(usize::MAX),
988            "index out of bounds ({} >= {})",
989            index,
990            obj.nbEntries
991        );
992        let array = unsafe { obj.__bindgen_anon_1.array };
993        unsafe { (*array.add(index)).as_keyed_object_mut() }
994    }
995}
996impl<K: AsRef<[u8]>, V: Into<WafObject>, const N: usize> From<[(K, V); N]> for WafMap {
997    fn from(vals: [(K, V); N]) -> Self {
998        let mut map = WafMap::new(N as u64);
999        for (i, (k, v)) in vals.into_iter().enumerate() {
1000            map[i] = Keyed::from((k.as_ref(), v));
1001        }
1002        map
1003    }
1004}
1005
1006impl fmt::Debug for WafBool {
1007    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1008        write!(f, "{}({})", stringify!(WafBool), self.value())
1009    }
1010}
1011impl From<bool> for WafBool {
1012    fn from(value: bool) -> Self {
1013        Self::new(value)
1014    }
1015}
1016
1017impl fmt::Debug for WafFloat {
1018    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1019        write!(f, "{}({})", stringify!(WafFloat), self.value())
1020    }
1021}
1022impl From<f64> for WafFloat {
1023    fn from(value: f64) -> Self {
1024        Self::new(value)
1025    }
1026}
1027
1028impl fmt::Debug for WafNull {
1029    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1030        write!(f, "{}", stringify!(WafNull))
1031    }
1032}
1033impl From<()> for WafNull {
1034    fn from((): ()) -> Self {
1035        Self::new()
1036    }
1037}
1038
1039/// An [`WafObject`] or [`TypedWafObject`] associated with a key.
1040#[repr(transparent)]
1041pub struct Keyed<T: AsRawMutObject> {
1042    value: T,
1043}
1044impl<T: AsRawMutObject> Keyed<T> {
1045    /// Creates a new [`Keyed<WafObject>`] wrapping the provided value, without setting the key..
1046    pub(crate) fn new(value: T) -> Self {
1047        Self { value }
1048    }
1049
1050    /// Obtains a reference to the wrapped value.
1051    pub fn inner(&self) -> &T {
1052        &self.value
1053    }
1054
1055    /// Obtains the key associated with this [`Keyed<WafObject>`], as-is.
1056    ///
1057    /// # Panics
1058    /// Panics if the underlying object's key is too long to be represented on this platform. This can only happen on
1059    /// platforms where [`usize`] is 32-bit wide.
1060    pub fn key(&self) -> &[u8] {
1061        let obj = self.as_ref();
1062        if obj.parameterNameLength == 0 {
1063            return &[];
1064        }
1065        let len =
1066            usize::try_from(obj.parameterNameLength).expect("key is too long for this platform");
1067        unsafe { std::slice::from_raw_parts(obj.parameterName.cast(), len) }
1068    }
1069
1070    /// Obtains the key associated with this [`Keyed<WafObject>`] as a string.
1071    ///
1072    /// # Errors
1073    /// Returns an error if the underlying key data is not a valid UTF-8 string, under the same conditions as
1074    /// [`std::str::from_utf8`].
1075    pub fn key_str(&self) -> Result<&str, std::str::Utf8Error> {
1076        std::str::from_utf8(self.key())
1077    }
1078
1079    /// Sets the key associated with this [`Keyed<WafObject>`].
1080    pub fn set_key(&mut self, key: &[u8]) -> &mut Self {
1081        let obj = unsafe { self.as_raw_mut() };
1082        unsafe { obj.drop_key() };
1083        if key.is_empty() {
1084            obj.parameterName = null_mut();
1085            obj.parameterNameLength = 0;
1086            return self;
1087        }
1088
1089        let b: Box<[u8]> = key.into();
1090        let ptr = Box::into_raw(b);
1091        obj.parameterName = ptr.cast();
1092        obj.parameterNameLength = key.len() as u64;
1093        self
1094    }
1095
1096    /// Sets the key associated with this [`Keyed<WafObject>`] to the provided string.
1097    pub fn set_key_str(&mut self, key: &str) -> &mut Self {
1098        self.set_key(key.as_bytes())
1099    }
1100}
1101impl Keyed<WafObject> {
1102    #[must_use]
1103    pub fn as_type<T: TypedWafObject>(&self) -> Option<&Keyed<T>> {
1104        if self.value.get_type() == T::TYPE {
1105            Some(unsafe { &*(std::ptr::from_ref(self).cast()) })
1106        } else {
1107            None
1108        }
1109    }
1110
1111    pub fn as_type_mut<T: TypedWafObject>(&mut self) -> Option<&mut Keyed<T>> {
1112        if self.value.get_type() == T::TYPE {
1113            Some(unsafe { &mut *(std::ptr::from_mut(self).cast()) })
1114        } else {
1115            None
1116        }
1117    }
1118}
1119// Note - We are not implementing DerefMut for Keyed as it'd allow leaking the key if it is used
1120// through [std::mem::take] or [std::mem::replace].
1121impl Keyed<WafArray> {
1122    pub fn iter(&self) -> impl Iterator<Item = &WafObject> {
1123        self.value.iter()
1124    }
1125
1126    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut WafObject> {
1127        self.value.iter_mut()
1128    }
1129}
1130// Note - We are not implementing DerefMut for Keyed as it'd allow leaking the key if it is used
1131// through [std::mem::take] or [std::mem::replace].
1132impl Keyed<WafMap> {
1133    pub fn iter(&self) -> impl Iterator<Item = &Keyed<WafObject>> {
1134        self.value.iter()
1135    }
1136
1137    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Keyed<WafObject>> {
1138        self.value.iter_mut()
1139    }
1140}
1141impl<T: AsRawMutObject> AsRawMutObject for Keyed<T> {
1142    unsafe fn as_raw_mut(&mut self) -> &mut libddwaf_sys::ddwaf_object {
1143        unsafe { self.value.as_raw_mut() }
1144    }
1145}
1146impl<T: AsRawMutObject> crate::private::Sealed for Keyed<T> {}
1147impl<T: AsRawMutObject> AsRef<libddwaf_sys::ddwaf_object> for Keyed<T> {
1148    fn as_ref(&self) -> &libddwaf_sys::ddwaf_object {
1149        self.value.as_ref()
1150    }
1151}
1152impl<T: Default + AsRawMutObject> std::default::Default for Keyed<T> {
1153    fn default() -> Self {
1154        Self {
1155            value: T::default(),
1156        }
1157    }
1158}
1159impl<T: AsRawMutObject> Deref for Keyed<T> {
1160    type Target = T;
1161    fn deref(&self) -> &Self::Target {
1162        &self.value
1163    }
1164}
1165impl<T: AsRawMutObject> std::ops::Drop for Keyed<T> {
1166    fn drop(&mut self) {
1167        unsafe { self.as_raw_mut().drop_key() };
1168        // self.value implicitly dropped
1169    }
1170}
1171impl<T: AsRawMutObject + fmt::Debug> fmt::Debug for Keyed<T> {
1172    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1173        write!(f, "\"{:?}\"={:?}", fmt_bin_str(self.key()), self.value)
1174    }
1175}
1176impl<T, U: AsRawMutObject> From<(&str, T)> for Keyed<U>
1177where
1178    T: Into<U>,
1179{
1180    fn from((key, value): (&str, T)) -> Self {
1181        let unkeyed = value.into();
1182        let mut keyed = Keyed::new(unkeyed);
1183        keyed.set_key_str(key);
1184        keyed
1185    }
1186}
1187impl<T, U: AsRawMutObject> From<(&[u8], T)> for Keyed<U>
1188where
1189    T: Into<U>,
1190{
1191    fn from((key, value): (&[u8], T)) -> Self {
1192        let unkeyed = value.into();
1193        let mut keyed = Keyed::new(unkeyed);
1194        keyed.set_key(key);
1195        keyed
1196    }
1197}
1198impl<T: TypedWafObject> From<Keyed<T>> for Keyed<WafObject> {
1199    fn from(value: Keyed<T>) -> Self {
1200        let res = Self {
1201            value: WafObject {
1202                raw: *value.as_ref(),
1203            },
1204        };
1205        std::mem::forget(value);
1206        res
1207    }
1208}
1209
1210trait UncheckedAsRef: crate::private::Sealed {
1211    /// Converts a naked reference to a [`libddwaf_sys::ddwaf_object`] into a reference to one of the
1212    /// user-friendlier types.
1213    ///
1214    /// # Safety
1215    /// The type `T` must be able to represent this [`libddwaf_sys::ddwaf_object`]'s type (per its
1216    /// associated [`libddwaf_sys::DDWAF_OBJ_TYPE`] value).
1217    unsafe fn unchecked_as_ref<T: AsRef<libddwaf_sys::ddwaf_object> + crate::private::Sealed>(
1218        &self,
1219    ) -> &T;
1220
1221    /// Converts a naked mutable reference to a `ddwaf_object` into a mutable reference to one of the
1222    ///
1223    /// # Safety
1224    /// - The type `T` must be able to represent this [`libddwaf_sys::ddwaf_object`]'s type (per its
1225    ///   associated [`libddwaf_sys::DDWAF_OBJ_TYPE`] value).
1226    /// - The destructor of `T` must be compatible with the value of self.
1227    unsafe fn unchecked_as_ref_mut<T: AsRef<libddwaf_sys::ddwaf_object> + crate::private::Sealed>(
1228        &mut self,
1229    ) -> &mut T;
1230}
1231impl crate::private::Sealed for libddwaf_sys::ddwaf_object {}
1232impl UncheckedAsRef for libddwaf_sys::ddwaf_object {
1233    unsafe fn unchecked_as_ref<T: AsRef<libddwaf_sys::ddwaf_object> + crate::private::Sealed>(
1234        &self,
1235    ) -> &T {
1236        unsafe { &*(std::ptr::from_ref(self).cast()) }
1237    }
1238
1239    unsafe fn unchecked_as_ref_mut<
1240        T: AsRef<libddwaf_sys::ddwaf_object> + crate::private::Sealed,
1241    >(
1242        &mut self,
1243    ) -> &mut T {
1244        unsafe { &mut *(std::ptr::from_mut(self).cast()) }
1245    }
1246}
1247trait UncheckedAsWafObject: crate::private::Sealed {
1248    /// Converts a naked reference to a [`libddwaf_sys::ddwaf_object`] into a reference to an [`WafObject`].
1249    fn as_object_ref(&self) -> &WafObject;
1250
1251    /// Converts a naked reference to a [`libddwaf_sys::ddwaf_object`] into a reference to an opaque
1252    /// [`Keyed<WafObject>`].
1253    fn as_keyed_object_ref(&self) -> &Keyed<WafObject>;
1254
1255    /// Converts a naked mutable reference to a [`libddwaf_sys::ddwaf_object`] into a mutable reference to
1256    /// an opaque [`Keyed<WafObject>`].
1257    ///
1258    /// # Safety
1259    /// The caller must ensure that the destructor of [`Keyed<WafObject>`]
1260    /// ([`libddwaf_sys::ddwaf_object::drop_key`] and [`libddwaf_sys::ddwaf_object::drop_object`]) can be called
1261    /// on self.
1262    unsafe fn as_keyed_object_mut(&mut self) -> &mut Keyed<WafObject>;
1263}
1264impl<T: UncheckedAsRef> UncheckedAsWafObject for T {
1265    /// Converts a naked reference to a [`libddwaf_sys::ddwaf_object`] into a reference to an [`WafObject`].
1266    fn as_object_ref(&self) -> &WafObject {
1267        unsafe { self.unchecked_as_ref::<WafObject>() }
1268    }
1269
1270    /// Converts a naked reference to a [`libddwaf_sys::ddwaf_object`] into a reference to an opaque
1271    /// [`Keyed<WafObject>`].
1272    fn as_keyed_object_ref(&self) -> &Keyed<WafObject> {
1273        // SAFETY: Keyed<WafObject> is compatible with all valid [libddwaf_sys::ddwaf_object] values,
1274        // event if their key is not set.
1275        unsafe { self.unchecked_as_ref::<Keyed<WafObject>>() }
1276    }
1277
1278    /// Converts a naked mutable reference to a [`libddwaf_sys::ddwaf_object`] into a mutable reference to
1279    /// an opaque [`Keyed<WafObject>`].
1280    ///
1281    /// # Safety
1282    /// The caller must ensure that the destructor of [`Keyed<WafObject>`]
1283    /// ([`libddwaf_sys::ddwaf_object::drop_key`] and [`libddwaf_sys::ddwaf_object::drop_object`]) can be called
1284    /// on self.
1285    unsafe fn as_keyed_object_mut(&mut self) -> &mut Keyed<WafObject> {
1286        unsafe { self.unchecked_as_ref_mut::<Keyed<WafObject>>() }
1287    }
1288}
1289
1290/// Formats a byte slice as an ASCII string, hex-escaping any non-printable characters.
1291fn fmt_bin_str(bytes: &[u8]) -> impl fmt::Debug + '_ {
1292    struct BinFormatter<'a>(&'a [u8]);
1293    impl fmt::Debug for BinFormatter<'_> {
1294        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1295            for &c in self.0 {
1296                if c == b'"' || c == b'\\' {
1297                    write!(f, "\\{}", c as char)?;
1298                } else if c.is_ascii_graphic() || c == b' ' {
1299                    write!(f, "{}", c as char)?;
1300                } else {
1301                    write!(f, "\\x{c:02X}")?;
1302                }
1303            }
1304            Ok(())
1305        }
1306    }
1307    BinFormatter(bytes)
1308}
1309
1310/// Helper macro to create [`WafObject`]s.
1311#[macro_export]
1312macro_rules! waf_object {
1313    (null) => {
1314        $crate::object::WafObject::from(())
1315    };
1316    ($l:expr) => {
1317        $crate::object::WafObject::from($l)
1318    };
1319}
1320
1321/// Helper macro to create [`WafArray`]s.
1322#[macro_export]
1323macro_rules! waf_array {
1324    () => { $crate::object::WafArray::new(0) };
1325    ($($e:expr),* $(,)?) => {
1326        {
1327            let size = [$($crate::__repl_expr_with_unit!($e)),*].len();
1328            let mut res = $crate::object::WafArray::new(size as u64);
1329            let mut i = usize::MAX;
1330            $(
1331                i = i.wrapping_add(1);
1332                res[i] = $crate::waf_object!($e);
1333            )*
1334            res
1335        }
1336    };
1337}
1338
1339/// Helper macro to create [`WafMap`]s.
1340#[macro_export]
1341macro_rules! waf_map {
1342    () => { $crate::object::WafMap::new(0) };
1343    ($(($k:literal, $v:expr)),* $(,)?) => {
1344        {
1345            let size = [$($crate::__repl_expr_with_unit!($v)),*].len();
1346            let mut res = $crate::object::WafMap::new(size as u64);
1347            let mut i = usize::MAX;
1348            $(
1349                i = i.wrapping_add(1);
1350                let k: &str = $k.into();
1351                let obj = $crate::object::Keyed::<$crate::object::WafObject>::from((k, $v));
1352                res[i] = obj.into();
1353            )*
1354            res
1355        }
1356    };
1357}
1358
1359/// Helper macro to facilitate counting token trees within other macros.
1360///
1361/// Not intended for use outside of this crate, but must be exported as it is used by macros in this crate.
1362#[doc(hidden)]
1363#[macro_export]
1364macro_rules! __repl_expr_with_unit {
1365    ($e:expr) => {
1366        ()
1367    };
1368}
1369
1370#[cfg(test)]
1371#[cfg_attr(coverage_nightly, coverage(off))]
1372mod tests {
1373    use std::str::FromStr;
1374
1375    use super::*;
1376
1377    #[test]
1378    #[allow(clippy::float_cmp)] // No operations are done on the values, they should be the same.
1379    fn unsafe_changes_to_default_objects() {
1380        unsafe {
1381            let mut unsigned = WafUnsigned::default();
1382            unsigned.as_raw_mut().__bindgen_anon_1.uintValue += 1;
1383            assert_eq!(unsigned.value(), 1);
1384
1385            let mut signed = WafSigned::default();
1386            signed.as_raw_mut().__bindgen_anon_1.intValue -= 1;
1387            assert_eq!(signed.value(), -1);
1388
1389            let mut float = WafFloat::default();
1390            float.as_raw_mut().__bindgen_anon_1.f64_ += 1.0;
1391            assert_eq!(float.value(), 1.0);
1392
1393            let mut boolean = WafBool::default();
1394            boolean.as_raw_mut().__bindgen_anon_1.boolean = true;
1395            assert!(boolean.value());
1396
1397            let mut null = WafNull::default();
1398            // nothing interesting to do for null; let's try manually setting
1399            // the parameter name
1400            let s = String::from_str("foobar").unwrap();
1401            let b: Box<[u8]> = s.as_bytes().into();
1402            let p = Box::<[u8]>::into_raw(b);
1403            let null_mut = null.as_raw_mut();
1404            null_mut.parameterName = p.cast();
1405            null_mut.parameterNameLength = s.len() as u64;
1406            drop(std::mem::take(null_mut.as_keyed_object_mut()));
1407
1408            let mut string = WafString::default();
1409            let str_mut = string.as_raw_mut();
1410            let b: Box<[u8]> = s.as_bytes().into();
1411            let p = Box::<[u8]>::into_raw(b);
1412            str_mut.drop_string();
1413            str_mut.__bindgen_anon_1.stringValue = p as *const _;
1414            str_mut.nbEntries = s.len() as u64;
1415            assert_eq!(string.as_str().unwrap(), "foobar");
1416            assert_eq!(string.len(), s.len());
1417            assert!(!string.is_empty());
1418        }
1419    }
1420}