falco_plugin/plugin/event/
async_event.rs

1use crate::plugin::event::EventSource;
2use falco_event::fields::{FromBytes, ToBytes};
3use std::ffi::CStr;
4use std::fmt::{Debug, Formatter};
5
6/// Asynchronous event
7///
8/// This is the event type generated by [async event](`crate::async_event`) plugins.
9/// This differs from the autogenerated `falco_event_schema::types::PPME_ASYNCEVENT_E` type
10/// in the following ways:
11/// - none of the fields are `Option`s
12/// - the name is just a `&CStr`
13/// - serde serialization/deserialization is not supported
14/// - does not depend on the whole `falco_event_schema` crate
15///
16/// It handles encoding and decoding the payload automatically, so you can use any type that can be
17/// converted from/to a byte buffer (including a raw `&[u8]`) as the payload.
18///
19/// To store an arbitrary type inside the payload, make sure the data implements [`FromBytes`],
20/// [`ToBytes`] and [`EventSource`], for example:
21/// ```
22/// use std::io::Write;
23/// use falco_event::events::{AnyEventPayload, RawEvent};
24/// use falco_event::fields::{FromBytes, FromBytesError, ToBytes};
25/// use falco_plugin::event::{EventSource, AsyncEvent};
26/// use falco_plugin::event::events::Event;
27///
28/// struct MyEvent {
29///     param1: u32,
30///     param2: u32,
31/// }
32///
33/// impl EventSource for MyEvent {
34///     const SOURCE: Option<&'static str> = Some("my_plugin");
35/// }
36///
37/// impl FromBytes<'_> for MyEvent {
38///     fn from_bytes(buf: &mut &[u8]) -> Result<Self, FromBytesError> {
39///         if buf.len() < 2 * size_of::<u32>() {
40///             return Err(FromBytesError::InvalidLength);
41///         }
42///
43///         let param1 = u32::from_le_bytes(buf[..size_of::<u32>()].try_into().unwrap());
44///         let param2 = u32::from_le_bytes(buf[size_of::<u32>()..].try_into().unwrap());
45///         *buf = &buf[2 * size_of::<u32>()..];
46///         Ok(MyEvent { param1, param2 })
47///     }
48/// }
49///
50/// impl ToBytes for MyEvent {
51///     fn binary_size(&self) -> usize {
52///         2 * size_of::<u32>()
53///     }
54///
55///    fn write<W: Write>(&self, mut writer: W) -> std::io::Result<()> {
56///         writer.write_all(&self.param1.to_le_bytes())?;
57///         writer.write_all(&self.param2.to_le_bytes())?;
58///         Ok(())
59///     }
60///
61///     fn default_repr() -> impl ToBytes {
62///         MyEvent { param1: 0, param2: 0 }
63///     }
64/// }
65///
66///# trait FakePluginTrait {
67///#     type Event<'a>: AnyEventPayload + TryFrom<&'a RawEvent<'a>> where Self: 'a;
68///# }
69///# struct FakePlugin;
70///# impl FakePluginTrait for FakePlugin {
71/// // in a plugin trait implementation:
72/// type Event<'a> = Event<AsyncEvent<'a, MyEvent>>;
73///# }
74/// ```
75///
76/// *Note*: this SDK also provides support for [JSON-encoded payloads](`crate::event::JsonPayload`)
77/// since it's already necessary to talk to the Falco Plugin API. JSON is not a good choice
78/// for high-volume events, as it takes a lot of space and is pretty slow, compared to binary
79/// formats. See the source of `plugin::event:json` for what is needed to support a different
80/// serialization format and consider using e.g. [bincode](https://crates.io/crates/bincode) instead.
81#[derive(Default, falco_event::EventPayload)]
82#[event_payload(
83    source = <T as EventSource>::SOURCE,
84    code = 402,
85    length_type = u32,
86    from_bytes_bound(where T: FromBytes<'raw_event>),
87    to_bytes_bound(where T: ToBytes))]
88pub struct AsyncEvent<'a, T>
89where
90    T: EventSource,
91{
92    /// ID of the plugin that generated this event
93    pub plugin_id: u32,
94    /// Name of the plugin that generated this event
95    pub name: &'a CStr,
96    /// Payload, as a custom type
97    pub data: T,
98}
99
100impl<'a, T> Debug for AsyncEvent<'a, T>
101where
102    T: EventSource + Debug,
103{
104    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
105        write!(
106            f,
107            "> async plugin_id={} name={} data={:?}",
108            self.plugin_id,
109            self.name.to_string_lossy(),
110            self.data
111        )
112    }
113}