falco_plugin/plugin/event/
json.rs

1use crate::event::EventSource;
2use falco_event::fields::{FromBytes, FromBytesError, ToBytes};
3use serde::de::DeserializeOwned;
4use serde::Serialize;
5use std::cell::RefCell;
6use std::fmt::Debug;
7use std::io::Write;
8
9/// A wrapper that enables JSON-encoded event payloads in [`crate::event::AsyncEvent`] and [`crate::event::PluginEvent`]
10///
11/// To store an arbitrary type as JSON inside the payload, make sure the data implements
12/// `Serialize` and `Deserialize` from `serde` and use `JsonPayload<T>` as the payload type:
13/// ```
14/// use falco_event::events::{AnyEventPayload, RawEvent};
15/// use falco_plugin::event::{EventSource, JsonPayload, PluginEvent};
16/// use falco_plugin::event::events::Event;
17///
18/// #[derive(serde::Serialize, serde::Deserialize)]
19/// struct MyEvent {
20///     param1: u32,
21///     param2: u32,
22/// }
23///
24/// impl EventSource for MyEvent {
25///     const SOURCE: Option<&'static str> = Some("my_plugin");
26/// }
27///
28///# trait FakePluginTrait {
29///#     type Event<'a>: AnyEventPayload + TryFrom<&'a RawEvent<'a>> where Self: 'a;
30///# }
31///# struct FakePlugin;
32///# impl FakePluginTrait for FakePlugin {
33/// // in a plugin trait implementation:
34/// type Event<'a> = Event<PluginEvent<JsonPayload<MyEvent>>>;
35///# }
36/// ```
37///
38/// *Note*: this SDK provides JSON support since it's already necessary to talk
39/// to the Falco Plugin API. JSON is not a good choice for high-volume events, as it takes
40/// a lot of space and is pretty slow, compared to binary formats. See the source
41/// of `plugin::event:json` for what is needed to support a different serialization format
42/// and consider using e.g. [bincode](https://crates.io/crates/bincode) instead.
43pub struct JsonPayload<T> {
44    inner: T,
45    serialized: RefCell<Option<Result<Vec<u8>, std::io::Error>>>,
46}
47
48impl<T> Debug for JsonPayload<T>
49where
50    T: Debug,
51{
52    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53        T::fmt(&self.inner, f)
54    }
55}
56
57impl<T> JsonPayload<T> {
58    /// Create a [`JsonPayload`] object from any data
59    pub fn new(inner: T) -> Self {
60        Self {
61            inner,
62            serialized: RefCell::new(None),
63        }
64    }
65
66    /// Get a reference to the data inside
67    pub fn get_ref(&self) -> &T {
68        &self.inner
69    }
70
71    /// Get a mutable reference to the data inside
72    pub fn get_mut(&mut self) -> &mut T {
73        self.serialized.replace(None);
74        &mut self.inner
75    }
76
77    /// Return the wrapped data, dropping the wrapper
78    pub fn into_inner(self) -> T {
79        self.inner
80    }
81}
82
83impl<T> JsonPayload<T>
84where
85    T: Serialize,
86{
87    fn update_serialized(&self) {
88        if self.serialized.borrow().is_none() {
89            self.serialized.replace(Some(
90                serde_json::to_vec(&self.inner).map_err(std::io::Error::from),
91            ));
92        }
93    }
94}
95
96impl<'a, T> FromBytes<'a> for JsonPayload<T>
97where
98    T: DeserializeOwned,
99{
100    fn from_bytes(buf: &mut &'a [u8]) -> Result<Self, FromBytesError> {
101        let value: T = serde_json::from_slice(buf).map_err(|e| FromBytesError::Other(e.into()))?;
102        Ok(Self::new(value))
103    }
104}
105
106impl<T> ToBytes for JsonPayload<T>
107where
108    T: Serialize,
109{
110    fn binary_size(&self) -> usize {
111        self.update_serialized();
112        match self.serialized.borrow().as_ref().unwrap() {
113            Ok(v) => v.len(),
114            Err(_) => 0,
115        }
116    }
117
118    fn write<W: Write>(&self, writer: W) -> std::io::Result<()> {
119        self.update_serialized();
120        match self.serialized.take().unwrap() {
121            Ok(v) => {
122                let ret = v.as_slice().write(writer).map(|_| ());
123                self.serialized.replace(Some(Ok(v)));
124                ret
125            }
126            Err(e) => Err(e),
127        }
128    }
129
130    fn default_repr() -> impl ToBytes {
131        &[] as &[u8]
132    }
133}
134
135impl<T> EventSource for JsonPayload<T>
136where
137    T: EventSource,
138{
139    const SOURCE: Option<&'static str> = T::SOURCE;
140}