falco_plugin/plugin/tables/
data.rs

1use crate::plugin::tables::table::raw::RawTable;
2use crate::tables::TablesInput;
3use falco_plugin_api::{
4    ss_plugin_bool, ss_plugin_field_type_FTYPE_UINT64, ss_plugin_state_data,
5    ss_plugin_state_type_SS_PLUGIN_ST_BOOL, ss_plugin_state_type_SS_PLUGIN_ST_INT16,
6    ss_plugin_state_type_SS_PLUGIN_ST_INT32, ss_plugin_state_type_SS_PLUGIN_ST_INT64,
7    ss_plugin_state_type_SS_PLUGIN_ST_INT8, ss_plugin_state_type_SS_PLUGIN_ST_STRING,
8    ss_plugin_state_type_SS_PLUGIN_ST_TABLE, ss_plugin_state_type_SS_PLUGIN_ST_UINT16,
9    ss_plugin_state_type_SS_PLUGIN_ST_UINT32, ss_plugin_state_type_SS_PLUGIN_ST_UINT8,
10    ss_plugin_table_field_t,
11};
12use num_derive::FromPrimitive;
13use std::borrow::Borrow;
14use std::ffi::{CStr, CString};
15use std::fmt::{Debug, Formatter};
16
17pub(in crate::plugin::tables) mod seal {
18    pub trait Sealed {}
19}
20
21/// Types usable as table keys and values
22#[non_exhaustive]
23#[repr(u32)]
24#[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive)]
25pub enum FieldTypeId {
26    /// 8-bit signed int
27    I8 = ss_plugin_state_type_SS_PLUGIN_ST_INT8,
28    /// 16-bit signed int
29    I16 = ss_plugin_state_type_SS_PLUGIN_ST_INT16,
30    /// 32-bit signed int
31    I32 = ss_plugin_state_type_SS_PLUGIN_ST_INT32,
32    /// 64-bit signed int
33    I64 = ss_plugin_state_type_SS_PLUGIN_ST_INT64,
34    /// 8-bit unsigned int
35    U8 = ss_plugin_state_type_SS_PLUGIN_ST_UINT8,
36    /// 16-bit unsigned int
37    U16 = ss_plugin_state_type_SS_PLUGIN_ST_UINT16,
38    /// 32-bit unsigned int
39    U32 = ss_plugin_state_type_SS_PLUGIN_ST_UINT32,
40    /// 64-bit unsigned int
41    U64 = ss_plugin_field_type_FTYPE_UINT64,
42    /// A printable buffer of bytes, NULL terminated
43    String = ss_plugin_state_type_SS_PLUGIN_ST_STRING,
44    /// A table
45    Table = ss_plugin_state_type_SS_PLUGIN_ST_TABLE,
46    /// A boolean value, 4 bytes.
47    Bool = ss_plugin_state_type_SS_PLUGIN_ST_BOOL,
48}
49
50/// # A trait describing types usable as table keys and values
51pub trait TableData: seal::Sealed {
52    /// The Falco plugin type id of the data
53    const TYPE_ID: FieldTypeId;
54
55    /// # Convert to the raw FFI representation
56    ///
57    /// **Note**: even though the signature specifies an owned value, this value technically
58    /// still borrows from `self`, as it contains raw pointers (for string values)
59    fn to_data(&self) -> ss_plugin_state_data;
60}
61
62/// # A trait describing types usable as table keys
63pub trait Key: TableData {
64    /// The type borrowed from the FFI representation
65    type Borrowed: ?Sized;
66
67    /// # Borrow from the raw FFI representation
68    ///
69    /// **Note**: this function only borrows the data and must return a reference.
70    /// This means that Self::Borrowed must be repr(C) and compatible
71    /// with the layout of `ss_plugin_state_data`, or otherwise constructible
72    /// as a reference from a pointer to the actual data (e.g. CStr from a *const c_char).
73    ///
74    /// # Safety
75    /// `data` must contain valid data of the correct type
76    unsafe fn from_data(data: &ss_plugin_state_data) -> &Self::Borrowed
77    where
78        Self: Borrow<Self::Borrowed>;
79}
80
81/// # A trait describing types usable as table values
82pub trait Value: TableData {
83    /// The type of metadata attached to each field of this type
84    ///
85    /// Usually `()`, except for table-valued fields
86    type AssocData;
87
88    /// The type actually retrieved as the field value
89    type Value<'a>
90    where
91        Self: 'a;
92
93    /// Hydrate a [`ss_plugin_state_data`] value into the Rust representation
94    ///
95    /// # Safety
96    /// Returns a value with arbitrary lifetime (cannot really express "until the next
97    /// call across the API boundary" in the type system) so don't go crazy with 'static
98    unsafe fn from_data_with_assoc<'a>(
99        data: &ss_plugin_state_data,
100        assoc: &Self::AssocData,
101    ) -> Self::Value<'a>;
102
103    /// Given a raw table, fetch the field's metadata
104    ///
105    /// The only interesting implementation is for `Box<Table>`, which gets all the fields
106    /// of a nested table and stores it in the subtable metadata. All others are no-ops.
107    ///
108    /// # Safety
109    /// Dereferences a raw pointer
110    unsafe fn get_assoc_from_raw_table(
111        table: &RawTable,
112        field: *mut ss_plugin_table_field_t,
113        tables_input: &TablesInput,
114    ) -> Result<Self::AssocData, anyhow::Error>;
115}
116
117macro_rules! impl_table_data_direct {
118    ($ty:ty => $field:ident: $type_id:expr) => {
119        impl seal::Sealed for $ty {}
120        impl TableData for $ty {
121            const TYPE_ID: FieldTypeId = $type_id;
122
123            fn to_data(&self) -> ss_plugin_state_data {
124                ss_plugin_state_data { $field: *self }
125            }
126        }
127
128        impl Key for $ty {
129            type Borrowed = $ty;
130
131            unsafe fn from_data(data: &ss_plugin_state_data) -> &Self {
132                unsafe { &data.$field }
133            }
134        }
135
136        impl Value for $ty {
137            type AssocData = ();
138            type Value<'a> = $ty;
139
140            unsafe fn from_data_with_assoc<'a>(
141                data: &ss_plugin_state_data,
142                _assoc: &Self::AssocData,
143            ) -> Self::Value<'a> {
144                unsafe { data.$field }
145            }
146
147            unsafe fn get_assoc_from_raw_table(
148                _table: &RawTable,
149                _field: *mut ss_plugin_table_field_t,
150                _tables_input: &TablesInput,
151            ) -> Result<Self::AssocData, anyhow::Error> {
152                Ok(())
153            }
154        }
155    };
156}
157
158impl_table_data_direct!(u8 => u8_: FieldTypeId::U8);
159impl_table_data_direct!(i8 => s8: FieldTypeId::I8);
160impl_table_data_direct!(u16 => u16_: FieldTypeId::U16);
161impl_table_data_direct!(i16 => s16: FieldTypeId::I16);
162impl_table_data_direct!(u32 => u32_: FieldTypeId::U32);
163impl_table_data_direct!(i32 => s32: FieldTypeId::I32);
164impl_table_data_direct!(u64 => u64_: FieldTypeId::U64);
165impl_table_data_direct!(i64 => s64: FieldTypeId::I64);
166
167/// # A boolean value to use in tables
168///
169/// The boolean type in the plugin API is defined as a 32-bit value, which does not
170/// necessarily correspond to the Rust [`bool`] type. Since we borrow the actual
171/// value, we cannot convert it on the fly to the native Rust type.
172///
173/// This type serves as a wrapper, exposing conversion methods to/from Rust bool.
174#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
175#[repr(transparent)]
176pub struct Bool(pub(crate) ss_plugin_bool);
177
178impl Debug for Bool {
179    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
180        f.debug_tuple("Bool").field(&bool::from(*self)).finish()
181    }
182}
183
184impl From<bool> for Bool {
185    fn from(value: bool) -> Self {
186        Self(value as ss_plugin_bool)
187    }
188}
189
190impl From<Bool> for bool {
191    fn from(value: Bool) -> Self {
192        value.0 != 0
193    }
194}
195
196impl seal::Sealed for Bool {}
197
198impl TableData for Bool {
199    const TYPE_ID: FieldTypeId = FieldTypeId::Bool;
200    fn to_data(&self) -> ss_plugin_state_data {
201        ss_plugin_state_data { b: self.0 }
202    }
203}
204
205impl Value for Bool {
206    type AssocData = ();
207    type Value<'a> = bool;
208
209    unsafe fn from_data_with_assoc<'a>(
210        data: &ss_plugin_state_data,
211        _assoc: &Self::AssocData,
212    ) -> Self::Value<'a> {
213        unsafe { data.b != 0 }
214    }
215
216    unsafe fn get_assoc_from_raw_table(
217        _table: &RawTable,
218        _field: *mut ss_plugin_table_field_t,
219        _tables_input: &TablesInput,
220    ) -> Result<Self::AssocData, anyhow::Error> {
221        Ok(())
222    }
223}
224
225impl Key for Bool {
226    type Borrowed = Bool;
227
228    unsafe fn from_data(data: &ss_plugin_state_data) -> &Self {
229        unsafe { std::mem::transmute(&data.b) }
230    }
231}
232
233impl seal::Sealed for CString {}
234
235impl TableData for CString {
236    const TYPE_ID: FieldTypeId = FieldTypeId::String;
237
238    fn to_data(&self) -> ss_plugin_state_data {
239        ss_plugin_state_data {
240            str_: self.as_ptr(),
241        }
242    }
243}
244
245impl Key for CString {
246    type Borrowed = CStr;
247
248    unsafe fn from_data(data: &ss_plugin_state_data) -> &CStr {
249        unsafe { CStr::from_ptr(data.str_) }
250    }
251}
252
253impl seal::Sealed for CStr {}
254
255impl TableData for CStr {
256    const TYPE_ID: FieldTypeId = FieldTypeId::String;
257
258    fn to_data(&self) -> ss_plugin_state_data {
259        ss_plugin_state_data {
260            str_: self.as_ptr(),
261        }
262    }
263}
264
265impl Value for CStr {
266    type AssocData = ();
267    type Value<'a> = &'a CStr;
268
269    unsafe fn from_data_with_assoc<'a>(
270        data: &ss_plugin_state_data,
271        _assoc: &Self::AssocData,
272    ) -> Self::Value<'a> {
273        unsafe { CStr::from_ptr(data.str_) }
274    }
275
276    unsafe fn get_assoc_from_raw_table(
277        _table: &RawTable,
278        _field: *mut ss_plugin_table_field_t,
279        _tables_input: &TablesInput,
280    ) -> Result<Self::AssocData, anyhow::Error> {
281        Ok(())
282    }
283}