falco_plugin/tables/import/mod.rs
1//! # Importing tables from other plugins (or Falco core)
2//!
3//! Your plugin can access tables exported by other plugins (or Falco core) by importing them.
4//! The recommended approach is to use the `#[derive(TableMetadata)]` macro for that purpose.
5//!
6//! You will probably want to define two additional type aliases, so that the full definition
7//! involves:
8//! - a type alias for the whole table
9//! - a type alias for a single table entry
10//! - a metadata struct, describing an entry (somewhat indirectly)
11//!
12//! For example:
13//!
14//! ```
15//! # use std::ffi::CStr;
16//! # use std::sync::Arc;
17//! # use falco_plugin::tables::import::{Entry, Field, Table, TableMetadata};
18//! #
19//! type NestedThing = Entry<Arc<NestedThingMetadata>>;
20//! type NestedThingTable = Table<u64, NestedThing>;
21//!
22//! #[derive(TableMetadata)]
23//! #[entry_type(NestedThing)]
24//! struct NestedThingMetadata {
25//! number: Field<u64, NestedThing>,
26//! }
27//!
28//! type ImportedThing = Entry<Arc<ImportedThingMetadata>>;
29//! type ImportedThingTable = Table<u64, ImportedThing>;
30//!
31//! #[derive(TableMetadata)]
32//! #[entry_type(ImportedThing)]
33//! struct ImportedThingMetadata {
34//! imported: Field<u64, ImportedThing>,
35//! nested: Field<NestedThingTable, ImportedThing>,
36//!
37//! #[name(c"type")]
38//! thing_type: Field<u64, ImportedThing>,
39//!
40//! #[custom]
41//! added: Field<CStr, ImportedThing>,
42//! }
43//!
44//! # // make this doctest a module, not a function: https://github.com/rust-lang/rust/issues/83583#issuecomment-1083300448
45//! # fn main() {}
46//! ```
47//!
48//! In contrast to [exported tables](`crate::tables::export`), the entry struct does not
49//! contain any accessible fields. It only provides generated methods to access each field
50//! using the plugin API. This means that each read/write is fairly expensive (involves
51//! method calls), so you should probably cache the values in local variables.
52//!
53//! ## Declaring fields
54//!
55//! You need to declare each field you're going to use in a particular table, by providing
56//! a corresponding [`Field`] field in the metadata struct. You do **not** need
57//! to declare all fields in the table, or put the fields in any particular order, but you
58//! **do** need to get the type right (otherwise you'll get an error at initialization time).
59//!
60//! The Falco table field name is the same as the field name in your metadata struct,
61//! unless overridden by `#[name(c"foo")]`. This is useful if a field's name is a Rust reserved
62//! word (e.g. `type`).
63//!
64//! You can also add fields to imported tables. To do that, tag the field with a `#[custom]`
65//! attribute. It will be then added to the table instead of looking it up in existing fields.
66//! Note that multiple plugins can add a field with the same name and type, which will make them
67//! all use the same field (they will share the data). Adding a field multiple times
68//! with different types is not allowed and will cause an error at initialization time.
69//!
70//! ## Generated methods
71//!
72//! Each scalar field gets a getter and setter method, e.g. declaring a metadata struct like
73//! the above example will generate the following methods **on the `ImportedThing` type**
74//! (for the scalar fields):
75//!
76//! ```ignore
77//! fn get_imported(&self, reader: &TableReader) -> Result<u64, anyhow::Error>;
78//! fn set_imported(&self, writer: &TableWriter, value: &u64) -> Result<(), anyhow::Error>;
79//!
80//! fn get_thing_type(&self, reader: &TableReader) -> Result<u64, anyhow::Error>;
81//! fn set_thing_type(&self, writer: &TableWriter, value: &u64) -> Result<(), anyhow::Error>;
82//!
83//! fn get_added<'a>(&'a self, reader: &TableReader) -> Result<&'a CStr, anyhow::Error>;
84//! fn set_added(&self, writer: &TableWriter, value: &CStr) -> Result<(), anyhow::Error>;
85//! ```
86//!
87//! Each table-typed field (nested table) gets a getter and a nested getter, so the above example
88//! will generate the following methods for the `nested` field:
89//!
90//! ```ignore
91//! fn get_nested(&self, reader: &TableReader) -> Result<NestedThingTable, anyhow::Error>;
92//! fn get_nested_by_key(&self, reader: &TableReader, key: &u64)
93//! -> Result<NestedThing, anyhow::Error>;
94//! ```
95//!
96//! **Note**: setters do not take `&mut self` as all the mutation happens on the other side
97//! of the API (presumably in another plugin).
98//!
99//! ### Visibility of generated methods
100//!
101//! The generated methods are actually trait implementations, not inherent impls (due to proc
102//! macro limitations). The traits in question are automatically generated and `use`d
103//! in the scope that defines the table struct. However, when you try to use them from
104//! a different module, you will get an error since they're not in scope (as traits need to
105//! be `use`d explicitly before their methods can be called).
106//!
107//! The module they're defined in has an autogenerated name (`__falco_plugin_private_<TABLE_TYPE_NAME>`)
108//! that doesn't look too nice when explicitly used, so for larger plugins (where the table
109//! access is split across modules) you can use the `#[accessors_mod]` attribute to choose
110//! a friendlier name for this module:
111//!
112//! ```
113//! pub mod imported_table {
114//! # use std::sync::Arc;
115//! # use falco_plugin::tables::import::{Entry, Field, Table, TableMetadata};
116//! #
117//! pub type ImportedThing = Entry<Arc<ImportedThingMetadata>>;
118//! type ImportedThingTable = Table<u64, ImportedThing>;
119//!
120//! #[derive(TableMetadata)]
121//! #[entry_type(ImportedThing)]
122//! #[accessors_mod(thing_accessors)]
123//! pub struct ImportedThingMetadata {
124//! imported: Field<u64, ImportedThing>,
125//! }
126//! }
127//!
128//! mod use_the_table {
129//! # use falco_plugin::tables::TableReader;
130//! # use falco_plugin::anyhow;
131//! use super::imported_table::ImportedThing;
132//! use super::imported_table::thing_accessors::*;
133//!
134//! fn use_the_table(thing: &ImportedThing, reader: &impl TableReader)
135//! -> Result<u64, anyhow::Error> {
136//! thing.get_imported(reader)
137//! }
138//! }
139//!
140//! # // make this doctest a module, not a function: https://github.com/rust-lang/rust/issues/83583#issuecomment-1083300448
141//! # fn main() {}
142//!
143//! ```
144//!
145//! # Example
146//!
147//! ```
148//! use std::ffi::CStr;
149//! use std::sync::Arc;
150//! use falco_plugin::anyhow::Error;
151//! use falco_plugin::base::Plugin;
152//! use falco_event::events::RawEvent;
153//! use falco_plugin::parse::{EventInput, ParseInput, ParsePlugin};
154//! use falco_plugin::tables::TablesInput;
155//! use falco_plugin::tables::import::{Entry, Field, Table, TableMetadata};
156//!# use falco_plugin::plugin;
157//!# use falco_plugin::parse_plugin;
158//!
159//! #[derive(TableMetadata)]
160//! #[entry_type(ImportedThing)]
161//! struct ImportedThingMetadata {
162//! imported: Field<u64, ImportedThing>,
163//!
164//! #[name(c"type")]
165//! thing_type: Field<u64, ImportedThing>,
166//!
167//! #[custom]
168//! added: Field<CStr, ImportedThing>,
169//! }
170//!
171//! type ImportedThing = Entry<Arc<ImportedThingMetadata>>;
172//! type ImportedThingTable = Table<u64, ImportedThing>;
173//!
174//! struct MyPlugin {
175//! things: ImportedThingTable,
176//! }
177//!
178//! impl Plugin for MyPlugin {
179//! // ...
180//!# const NAME: &'static CStr = c"dummy_extract";
181//!# const PLUGIN_VERSION: &'static CStr = c"0.0.0";
182//!# const DESCRIPTION: &'static CStr = c"test plugin";
183//!# const CONTACT: &'static CStr = c"rust@localdomain.pl";
184//!# type ConfigType = ();
185//!
186//! fn new(input: Option<&TablesInput>, _config: Self::ConfigType) -> Result<Self, Error> {
187//! let input = input.ok_or_else(|| anyhow::anyhow!("did not get table input"))?;
188//! let things: ImportedThingTable = input.get_table(c"things")?;
189//!
190//! Ok(Self { things })
191//! }
192//! }
193//!
194//! impl ParsePlugin for MyPlugin {
195//! type Event<'a> = RawEvent<'a>;
196//!
197//! fn parse_event(&mut self, event: &EventInput<RawEvent>, parse_input: &ParseInput)
198//! -> anyhow::Result<()> {
199//! // creating and accessing entries
200//! let reader = &parse_input.reader;
201//! let writer = &parse_input.writer;
202//!
203//! // create a new entry (not yet attached to a table key)
204//! let entry = self.things.create_entry(writer)?;
205//! entry.set_imported(writer, &5u64)?;
206//!
207//! // attach the entry to a table key
208//! self.things.insert(reader, writer, &1u64, entry)?;
209//!
210//! // look up the entry we have just added
211//! let entry = self.things.get_entry(reader, &1u64)?;
212//! assert_eq!(entry.get_imported(reader).ok(), Some(5u64));
213//!
214//! Ok(())
215//! }
216//! }
217//!
218//! # plugin!(MyPlugin);
219//! # parse_plugin!(MyPlugin);
220//! # // make this doctest a module, not a function: https://github.com/rust-lang/rust/issues/83583#issuecomment-1083300448
221//! # fn main() {}
222//! ```
223//!
224//! **Note**: The derive macro involves creating a private module (to avoid polluting
225//! the top-level namespace with a bunch of one-off traits), so you cannot use it inside
226//! a function due to scoping issues. See <https://github.com/rust-lang/rust/issues/83583>
227//! for details.
228//!
229//! # Bypassing the derive macro
230//!
231//! The derive macro boils down to automatically calling get_field/add_field for each
232//! field defined in the metadata struct (and generating getters/setters). If you don't know
233//! the field names in advance (e.g. when supporting different versions of "parent" plugins),
234//! there is the [`RuntimeEntry`] type alias, which makes you responsible for holding
235//! the field structs (probably in your plugin type) and requires you to use the generic
236//! read_field/write_field methods, in exchange for the flexibility.
237//!
238//! The above example can be rewritten without the derive macro as follows:
239//!
240//! ```
241//! use std::ffi::CStr;
242//! use falco_event::events::RawEvent;
243//! use falco_plugin::anyhow::Error;
244//! use falco_plugin::base::Plugin;
245//! use falco_plugin::parse::{EventInput, ParseInput, ParsePlugin};
246//!# use falco_plugin::{parse_plugin, plugin};
247//! use falco_plugin::tables::TablesInput;
248//! use falco_plugin::tables::import::{Field, RuntimeEntry, Table};
249//!
250//! struct ImportedThingTag;
251//! type ImportedThing = RuntimeEntry<ImportedThingTag>;
252//! type ImportedThingTable = Table<u64, ImportedThing>;
253//!
254//! struct MyPlugin {
255//! things: ImportedThingTable,
256//! thing_imported_field: Field<u64, ImportedThing>,
257//! thing_type_field: Field<u64, ImportedThing>,
258//! thing_added_field: Field<CStr, ImportedThing>,
259//! }
260//!
261//! impl Plugin for MyPlugin {
262//! // ...
263//!# const NAME: &'static CStr = c"dummy_extract";
264//!# const PLUGIN_VERSION: &'static CStr = c"0.0.0";
265//!# const DESCRIPTION: &'static CStr = c"test plugin";
266//!# const CONTACT: &'static CStr = c"rust@localdomain.pl";
267//!# type ConfigType = ();
268//!
269//! fn new(input: Option<&TablesInput>, _config: Self::ConfigType) -> Result<Self, Error> {
270//! let input = input.ok_or_else(|| anyhow::anyhow!("did not get table input"))?;
271//! let things: ImportedThingTable = input.get_table(c"things")?;
272//! let thing_imported_field = things.get_field(input, c"imported")?;
273//! let thing_type_field = things.get_field(input, c"type")?;
274//! let thing_added_field = things.add_field(input, c"added")?;
275//!
276//! Ok(Self {
277//! things,
278//! thing_imported_field,
279//! thing_type_field,
280//! thing_added_field,
281//! })
282//! }
283//! }
284//!
285//! impl ParsePlugin for MyPlugin {
286//! type Event<'a> = RawEvent<'a>;
287//!
288//! fn parse_event(&mut self, event: &EventInput<RawEvent>, parse_input: &ParseInput)
289//! -> anyhow::Result<()> {
290//! // creating and accessing entries
291//! let reader = &parse_input.reader;
292//! let writer = &parse_input.writer;
293//!
294//! // create a new entry (not yet attached to a table key)
295//! let entry = self.things.create_entry(writer)?;
296//! entry.write_field(writer, &self.thing_imported_field, &5u64)?;
297//!
298//! // attach the entry to a table key
299//! self.things.insert(reader, writer, &1u64, entry)?;
300//!
301//! // look up the entry we have just added
302//! let entry = self.things.get_entry(reader, &1u64)?;
303//! assert_eq!(
304//! entry.read_field(reader, &self.thing_imported_field).ok(),
305//! Some(5u64),
306//! );
307//!
308//! Ok(())
309//! }
310//! }
311//!# plugin!(MyPlugin);
312//!# parse_plugin!(MyPlugin);
313//! ```
314//!
315//! **Note**: in the above example, `ImportedThingTag` is just an empty struct, used to
316//! distinguish entries (and fields) from different types between one another. You can
317//! skip this and do not pass the second generic argument to `Field` and `Table`
318//! (it will default to `RuntimeEntry<()>`), but you lose compile time validation for
319//! accessing fields from the wrong table. It will still be caught at runtime.
320//!
321//! See the [`Table`] type for additional methods on tables, to e.g. iterate
322//! over entries or clear the whole table.
323
324mod entry;
325mod field;
326mod macros;
327mod runtime;
328mod runtime_table_validator;
329mod table;
330mod table_input;
331
332// for macro use only
333#[doc(hidden)]
334pub mod traits;
335
336pub use crate::tables::data::Bool;
337pub use crate::tables::data::TableData;
338pub use entry::Entry;
339pub use field::Field;
340pub use runtime::RuntimeEntry;
341pub use table::Table;
342
343// for macro use only
344#[doc(hidden)]
345pub use table::raw::RawTable;
346
347/// Mark a struct type as an imported table entry metadata
348///
349/// See the [module documentation](`crate::tables::import`) for details.
350pub use falco_plugin_derive::TableMetadata;