Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions falco_plugin/src/tables/import/field_info.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use crate::tables::FieldTypeId;
use num_traits::FromPrimitive;
use std::fmt::Debug;

/// Information about a table field
#[repr(transparent)]
pub struct FieldInfo(falco_plugin_api::ss_plugin_table_fieldinfo);

impl Debug for FieldInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FieldInfo")
.field("name", &self.name())
.field("read_only", &self.read_only())
.field("field_type", &self.field_type())
.finish()
}
}

impl FieldInfo {
/// Returns the name of the field
///
/// If the field name cannot be represented as UTF-8, returns "&lt;invalid field name&gt;"
pub fn name(&self) -> &str {
unsafe { std::ffi::CStr::from_ptr(self.0.name) }
.to_str()
.unwrap_or("<invalid field name>")
Comment thread
gnosek marked this conversation as resolved.
}
Comment thread
gnosek marked this conversation as resolved.

/// Returns true if the current field is read-only
pub fn read_only(&self) -> bool {
self.0.read_only != 0
}

/// Returns the type of the field
///
/// If the type cannot be represented as a [`FieldTypeId`], returns an error
/// with the raw field type value.
pub fn field_type(&self) -> Result<FieldTypeId, u32> {
match FieldTypeId::from_u32(self.0.field_type) {
Some(field_type) => Ok(field_type),
None => Err(self.0.field_type),
}
}
}
4 changes: 4 additions & 0 deletions falco_plugin/src/tables/import/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,10 +323,12 @@

mod entry;
mod field;
mod field_info;
mod macros;
mod runtime;
mod runtime_table_validator;
mod table;
mod table_info;
mod table_input;

// for macro use only
Expand All @@ -337,8 +339,10 @@ pub use crate::tables::data::Bool;
pub use crate::tables::data::TableData;
pub use entry::Entry;
pub use field::Field;
pub use field_info::FieldInfo;
pub use runtime::RuntimeEntry;
pub use table::Table;
pub use table_info::TableInfo;

// for macro use only
#[doc(hidden)]
Expand Down
13 changes: 4 additions & 9 deletions falco_plugin/src/tables/import/table/mod.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
use crate::tables::data::{seal, FieldTypeId, Key, TableData, Value};
use crate::tables::import::entry;
use crate::tables::import::field::Field;
use crate::tables::import::runtime::NoMetadata;
use crate::tables::import::runtime_table_validator::RuntimeTableValidator;
use crate::tables::import::table::raw::{IterationResult, RawTable};
use crate::tables::import::traits::{Entry, TableAccess, TableMetadata};
use crate::tables::TableFields;
use crate::tables::import::{entry, FieldInfo};
use crate::tables::TableReader;
use crate::tables::TableWriter;
use crate::tables::TablesInput;
use anyhow::Error;
use falco_plugin_api::{ss_plugin_state_data, ss_plugin_table_field_t, ss_plugin_table_fieldinfo};
use falco_plugin_api::{ss_plugin_state_data, ss_plugin_table_field_t};
use std::ffi::CStr;
use std::marker::PhantomData;
use std::ops::ControlFlow;
Expand Down Expand Up @@ -145,12 +144,8 @@ where
}

/// # List the available fields
///
/// **Note**: this method is of limited utility in actual plugin code (you know the fields you
/// want to access), so it returns the unmodified structure from the plugin API, including
/// raw pointers to C-style strings. This may change later.
pub fn list_fields(&self, fields_vtable: &TableFields) -> &[ss_plugin_table_fieldinfo] {
self.raw_table.list_fields(fields_vtable)
pub fn list_fields(&self, tables_input: &TablesInput) -> &[FieldInfo] {
self.raw_table.list_fields(tables_input)
}

/// # Get a table field by name
Expand Down
25 changes: 14 additions & 11 deletions falco_plugin/src/tables/import/table/raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ use crate::tables::data::{FieldTypeId, Key, Value};
use crate::tables::import::entry::raw::RawEntry;
use crate::tables::import::field::raw::RawField;
use crate::tables::import::traits::TableMetadata;
use crate::tables::TableFields;
use crate::tables::import::FieldInfo;
use crate::tables::TableReader;
use crate::tables::TableReaderImpl;
use crate::tables::TableWriter;
use crate::tables::TableWriterImpl;
use crate::tables::TablesInput;
use falco_plugin_api::{
ss_plugin_bool, ss_plugin_rc_SS_PLUGIN_SUCCESS, ss_plugin_state_data, ss_plugin_state_type,
ss_plugin_table_entry_t, ss_plugin_table_field_t, ss_plugin_table_fieldinfo,
ss_plugin_table_iterator_func_t, ss_plugin_table_iterator_state_t, ss_plugin_table_t,
ss_plugin_table_entry_t, ss_plugin_table_field_t, ss_plugin_table_iterator_func_t,
ss_plugin_table_iterator_state_t, ss_plugin_table_t,
};
use num_traits::FromPrimitive;
use std::ffi::CStr;
Expand Down Expand Up @@ -65,19 +65,22 @@ pub struct RawTable {

impl RawTable {
/// # List the available fields
///
/// **Note**: this method is of limited utility in actual plugin code (you know the fields you
/// want to access), so it returns the unmodified structure from the plugin API, including
/// raw pointers to C-style strings. This may change later.
pub fn list_fields(&self, fields_vtable: &TableFields) -> &[ss_plugin_table_fieldinfo] {
pub fn list_fields(&self, tables_input: &TablesInput) -> &[FieldInfo] {
let mut num_fields = 0u32;
let fields = fields_vtable
let fields = tables_input
.fields_ext
.list_table_fields(self.table, &mut num_fields as *mut _)
.unwrap_or(std::ptr::null_mut());
.unwrap_or(std::ptr::null());
if fields.is_null() {
&[]
} else {
unsafe { std::slice::from_raw_parts(fields, num_fields as usize) }
// SAFETY: the plugin API guarantees that `fields` is valid and points
// to an array of `num_fields` `ss_plugin_table_fieldinfo` elements.
// `FieldInfo` is a `#[repr(transparent)]` wrapper around
// `ss_plugin_table_fieldinfo`, so casting the pointer to `FieldInfo`
// preserves the layout, and `from_raw_parts` can build a slice over
// those `num_fields` elements.
unsafe { std::slice::from_raw_parts(fields.cast(), num_fields as usize) }
}
}

Expand Down
38 changes: 38 additions & 0 deletions falco_plugin/src/tables/import/table_info.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use crate::tables::FieldTypeId;
use num_traits::FromPrimitive;
use std::fmt::Debug;

/// Information about a table
#[repr(transparent)]
pub struct TableInfo(falco_plugin_api::ss_plugin_table_info);

impl Debug for TableInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TableInfo")
.field("name", &self.name())
.field("key_type", &self.key_type())
.finish()
}
}

impl TableInfo {
/// Returns the name of the table
///
/// If the table name cannot be represented as UTF-8, returns "&lt;invalid table name&gt;"
pub fn name(&self) -> &str {
unsafe { std::ffi::CStr::from_ptr(self.0.name) }
.to_str()
.unwrap_or("<invalid table name>")
Comment thread
gnosek marked this conversation as resolved.
}
Comment thread
gnosek marked this conversation as resolved.

/// Returns the type of the table's key
///
/// If the type cannot be represented as a [`FieldTypeId`], returns an error
/// with the raw field type value.
pub fn key_type(&self) -> Result<FieldTypeId, u32> {
match FieldTypeId::from_u32(self.0.key_type) {
Some(field_type) => Ok(field_type),
None => Err(self.0.key_type),
}
}
}
13 changes: 12 additions & 1 deletion falco_plugin/src/tables/import/table_input.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::error::as_result::WithLastError;
use crate::tables::import::traits::{TableAccess, TableMetadata};
use crate::tables::import::RawTable;
use crate::tables::import::{RawTable, TableInfo};
use crate::tables::{Key, TablesInput};
use falco_plugin_api::ss_plugin_state_type;
use std::ffi::CStr;
Expand Down Expand Up @@ -31,4 +31,15 @@ impl TablesInput<'_> {
Ok(T::new(table, metadata, false))
}
}

/// # List the available tables
pub fn list_tables(&self) -> &[TableInfo] {
let mut num_tables = 0u32;
let tables = unsafe { (self.list_tables)(self.owner, &mut num_tables as *mut _) };
if tables.is_null() {
&[]
} else {
unsafe { std::slice::from_raw_parts(tables.cast(), num_tables as usize) }
}
}
}
6 changes: 1 addition & 5 deletions falco_plugin/src/tables/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@
//! can use them from your plugin (e.g. in a separate thread) concurrently to other plugins
//! (in the main thread).

pub(crate) use vtable::fields::TableFields;
pub use crate::tables::data::FieldTypeId;
pub(crate) use vtable::reader::private::TableReaderImpl;
pub use vtable::reader::LazyTableReader;
pub use vtable::reader::TableReader;
Expand All @@ -157,7 +157,3 @@ mod vtable;
// for macro use only
#[doc(hidden)]
pub use crate::tables::data::{Key, Value};

// for macro use only
#[doc(hidden)]
pub use crate::tables::data::FieldTypeId;
17 changes: 0 additions & 17 deletions falco_plugin/src/tables/vtable/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,20 +99,3 @@ impl TablesInput<'_> {
}
}
}

impl TablesInput<'_> {
/// # List the available tables
///
/// **Note**: this method is of limited utility in actual plugin code (you know the tables you
/// want to access), so it returns the unmodified structure from the plugin API, including
/// raw pointers to C-style strings. This may change later.
pub fn list_tables(&self) -> &[ss_plugin_table_info] {
let mut num_tables = 0u32;
let tables = unsafe { (self.list_tables)(self.owner, &mut num_tables as *mut _) };
if tables.is_null() {
&[]
} else {
unsafe { std::slice::from_raw_parts(tables, num_tables as usize) }
}
}
}
88 changes: 88 additions & 0 deletions falco_plugin_tests/tests/list_fields.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
use anyhow::Error;
use falco_plugin::base::Plugin;
use falco_plugin::event::events::Event;
use falco_plugin::event::{EventInput, PluginEvent};
use falco_plugin::parse::{ParseInput, ParsePlugin};
use falco_plugin::static_plugin;
use falco_plugin::tables::{FieldTypeId, TablesInput};
use falco_plugin_tests::plugin_collection::events::countdown::Countdown;
use falco_plugin_tests::plugin_collection::parse::remaining_into_table_direct::PARSE_REMAINING_INTO_TABLE_DIRECT_PLUGIN_API;
use falco_plugin_tests::plugin_collection::source::countdown::COUNTDOWN_PLUGIN_API;
use falco_plugin_tests::plugin_collection::tables::remaining_import::RemainingCounterImportTable;
use falco_plugin_tests::{init_plugin, instantiate_tests, TestDriver};
use std::ffi::CStr;

struct ListFieldsPlugin {
#[allow(unused)]
remaining_table_import: RemainingCounterImportTable,
}

impl Plugin for ListFieldsPlugin {
const NAME: &'static CStr = c"dummy";
const PLUGIN_VERSION: &'static CStr = c"0.0.0";
const DESCRIPTION: &'static CStr = c"test plugin";
const CONTACT: &'static CStr = c"rust@localdomain.pl";
type ConfigType = ();

fn new(input: Option<&TablesInput>, _config: Self::ConfigType) -> Result<Self, Error> {
let input = input.ok_or_else(|| anyhow::anyhow!("did not get table input"))?;

let remaining_table_import: RemainingCounterImportTable = input.get_table(c"remaining")?;

let fields = remaining_table_import.list_fields(input);
let mut num_fields = 0;
for field in fields {
match field.name() {
"remaining" => {
anyhow::ensure!(field.read_only() == false);
anyhow::ensure!(field.field_type() == Ok(FieldTypeId::U64));
num_fields += 1;
}
"readonly" => {
anyhow::ensure!(field.read_only() == true);
anyhow::ensure!(field.field_type() == Ok(FieldTypeId::U64));
num_fields += 1;
}
"countdown" => {
anyhow::ensure!(field.read_only() == true);
anyhow::ensure!(field.field_type() == Ok(FieldTypeId::Table));
num_fields += 1;
}
name => panic!("unknown field: {name}"),
}
}
anyhow::ensure!(num_fields == 3);

Comment thread
gnosek marked this conversation as resolved.
Ok(Self {
remaining_table_import,
})
}
}

impl ParsePlugin for ListFieldsPlugin {
type Event<'a> = Event<PluginEvent<Countdown<'a>>>;

fn parse_event(
&mut self,
_event: &EventInput<Self::Event<'_>>,
_parse_input: &ParseInput,
) -> anyhow::Result<()> {
Ok(())
}
}

static_plugin!(pub LIST_FIELDS_API = ListFieldsPlugin);

fn test_list_fields<D: TestDriver>() {
let (mut driver, _plugin) = init_plugin::<D>(
&COUNTDOWN_PLUGIN_API,
cr#"{"remaining": 4, "batch_size": 4}"#,
)
.unwrap();
driver
.register_plugin(&PARSE_REMAINING_INTO_TABLE_DIRECT_PLUGIN_API, c"")
.unwrap();
driver.register_plugin(&LIST_FIELDS_API, c"").unwrap();
}

instantiate_tests!(test_list_fields);
Loading
Loading