falco_plugin/plugin/extract/
schema.rs

1use crate::plugin::extract::extractor_fn::{ExtractLambda, ExtractorFn};
2use crate::plugin::extract::fields::{Extract, ExtractFieldTypeId};
3use crate::plugin::extract::ExtractPlugin;
4use serde::ser::SerializeStruct;
5use serde::{Serialize, Serializer};
6use std::fmt::{Debug, Formatter};
7
8/// The type of argument a field extractor expects
9///
10/// If a request comes with an argument not conforming to the spec
11/// (e.g. an argument where none was requested), the SDK will return an error
12/// and not invoke the extractor function at all.
13#[derive(Clone, Copy, Debug)]
14pub enum ExtractArgType {
15    /// no argument, extraction requested as `field_name`
16    None,
17    /// optional integer argument, extraction requested as `field_name` or `field_name[1]`
18    OptionalIndex,
19    /// optional string argument, extraction requested as `field_name` or `field_name[foo]`
20    OptionalKey,
21    /// required integer argument, extraction requested as `field_name[1]`
22    RequiredIndex,
23    /// required string argument, extraction requested as `field_name[foo]`
24    RequiredKey,
25}
26
27impl Serialize for ExtractArgType {
28    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
29    where
30        S: Serializer,
31    {
32        match self {
33            ExtractArgType::None => serializer.serialize_none(),
34            ExtractArgType::OptionalIndex => {
35                let mut ss = serializer.serialize_struct("arg", 1)?;
36                ss.serialize_field("isIndex", &true)?;
37                ss.end()
38            }
39            ExtractArgType::OptionalKey => {
40                let mut ss = serializer.serialize_struct("arg", 1)?;
41                ss.serialize_field("isKey", &true)?;
42                ss.end()
43            }
44            ExtractArgType::RequiredIndex => {
45                let mut ss = serializer.serialize_struct("arg", 2)?;
46                ss.serialize_field("isIndex", &true)?;
47                ss.serialize_field("isRequired", &true)?;
48                ss.end()
49            }
50            ExtractArgType::RequiredKey => {
51                let mut ss = serializer.serialize_struct("arg", 2)?;
52                ss.serialize_field("isKey", &true)?;
53                ss.serialize_field("isRequired", &true)?;
54                ss.end()
55            }
56        }
57    }
58}
59
60pub fn serialize_field_type<S: Serializer>(
61    f: &ExtractFieldTypeId,
62    serializer: S,
63) -> Result<S::Ok, S::Error> {
64    match f {
65        ExtractFieldTypeId::U64 => serializer.serialize_str("uint64"),
66        ExtractFieldTypeId::String => serializer.serialize_str("string"),
67        ExtractFieldTypeId::RelTime => serializer.serialize_str("reltime"),
68        ExtractFieldTypeId::AbsTime => serializer.serialize_str("abstime"),
69        ExtractFieldTypeId::Bool => serializer.serialize_str("bool"),
70        ExtractFieldTypeId::IpAddr => serializer.serialize_str("ipaddr"),
71        ExtractFieldTypeId::IpNet => serializer.serialize_str("ipnet"),
72    }
73}
74
75fn is_false(b: &bool) -> bool {
76    !*b
77}
78
79/// # A description of an extracted field
80///
81/// You should create instances of this struct by calling [`field`].
82///
83/// This struct is used to automatically generate the schema definition for the Falco plugin framework
84#[derive(Serialize)]
85pub struct ExtractFieldInfo<P: ExtractPlugin> {
86    /// the name of the extracted field, generally of the form `<plugin>.<field>`
87    pub name: &'static str,
88    #[serde(rename = "type")]
89    #[serde(serialize_with = "serialize_field_type")]
90    /// the type of the extracted field
91    pub field_type: ExtractFieldTypeId,
92    #[serde(rename = "isList")]
93    /// if true, the extract function returns a [`Vec`] of values, not a single one
94    pub is_list: bool,
95    /// the type of argument the extract function takes
96    pub arg: ExtractArgType,
97    #[serde(rename = "display")]
98    /// the display name for the extracted field, defaulting to the name
99    pub display_name: Option<&'static str>,
100    #[serde(rename = "desc")]
101    /// a description for the extracted field, mandatory but defaults to the name
102    pub description: &'static str,
103    #[serde(rename = "addOutput")]
104    #[serde(skip_serializing_if = "is_false")]
105    /// suggest that this field be included in output for compatible event sources
106    pub add_output: bool,
107    #[serde(skip)]
108    /// the function implementing the actual extraction
109    pub func: ExtractLambda<P>,
110}
111
112impl<P: ExtractPlugin> Debug for ExtractFieldInfo<P> {
113    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
114        let json = serde_json::to_string_pretty(self).map_err(|_| std::fmt::Error)?;
115        f.write_str(&json)
116    }
117}
118
119impl<P: ExtractPlugin> ExtractFieldInfo<P> {
120    /// Set the display name fdr the extracted field
121    pub const fn with_display(mut self, display_name: &'static str) -> Self {
122        self.display_name = Some(display_name);
123        self
124    }
125
126    /// Set the description for the extracted field
127    pub const fn with_description(mut self, description: &'static str) -> Self {
128        self.description = description;
129        self
130    }
131
132    /// Suggest this field to be appended to the output string for compatible event sources
133    pub const fn add_output(mut self) -> Self {
134        self.add_output = true;
135        self
136    }
137}
138
139/// Wrap a function or method to make it usable as a field extractor
140///
141/// See [ExtractPlugin::EXTRACT_FIELDS](`crate::extract::ExtractPlugin::EXTRACT_FIELDS`)
142pub const fn field<P, R, F, A>(name: &'static str, func: &'static F) -> ExtractFieldInfo<P>
143where
144    P: ExtractPlugin,
145    R: Extract + 'static,
146    F: ExtractorFn<P, R, A>,
147    A: 'static,
148{
149    ExtractFieldInfo {
150        name,
151        field_type: <R as Extract>::TYPE_ID,
152        is_list: <R as Extract>::IS_LIST,
153        arg: F::ARG_TYPE,
154        display_name: None,
155        description: name,
156        add_output: false,
157        func: ExtractLambda {
158            obj: func as *const _ as *const (),
159            func: F::extract,
160        },
161    }
162}