falco_plugin/base/
schema.rs1use schemars::{schema_for, JsonSchema};
2use serde::de::DeserializeOwned;
3use std::any::TypeId;
4use std::collections::BTreeMap;
5use std::ffi::{CStr, CString};
6use std::sync::Mutex;
7use thiserror::Error;
8
9#[derive(Error, Debug)]
10pub enum SchemaError {
11 #[error("JSON deserialization error: {0}")]
12 JsonError(#[from] serde_json::Error),
13}
14
15pub type SchemaResult<T> = Result<T, SchemaError>;
16
17#[derive(Debug)]
18pub enum ConfigSchemaType {
19 None,
20 Json(&'static CStr),
21}
22
23#[derive(Debug)]
28pub struct Json<T: JsonSchema + DeserializeOwned>(pub T);
29
30pub trait ConfigSchema: Sized {
31 fn get_schema() -> ConfigSchemaType;
32
33 fn from_str(s: &str) -> SchemaResult<Self>;
34}
35
36impl<T: JsonSchema + DeserializeOwned + 'static> ConfigSchema for Json<T> {
37 fn get_schema() -> ConfigSchemaType {
38 static CONFIG_SCHEMA: Mutex<BTreeMap<TypeId, CString>> = Mutex::new(BTreeMap::new());
39
40 let ty = TypeId::of::<Self>();
41 let mut schema_map = CONFIG_SCHEMA.lock().unwrap();
42 let ptr = unsafe {
49 CStr::from_ptr(
50 schema_map
51 .entry(ty)
52 .or_insert_with(|| {
53 let schema = schema_for!(T);
54 let schema = serde_json::to_string_pretty(&schema)
55 .expect("failed to serialize config schema");
56 CString::new(schema.into_bytes())
57 .expect("failed to add NUL to config schema")
58 })
59 .as_ptr(),
60 )
61 };
62
63 ConfigSchemaType::Json(ptr)
64 }
65
66 fn from_str(s: &str) -> SchemaResult<Self> {
67 let target: T = serde_json::from_str(s)?;
68 Ok(Json(target))
69 }
70}
71
72impl ConfigSchema for String {
73 fn get_schema() -> ConfigSchemaType {
74 ConfigSchemaType::None
75 }
76
77 fn from_str(s: &str) -> SchemaResult<Self> {
78 Ok(s.to_string())
79 }
80}
81
82impl ConfigSchema for () {
83 fn get_schema() -> ConfigSchemaType {
84 ConfigSchemaType::None
85 }
86
87 fn from_str(_: &str) -> SchemaResult<Self> {
88 Ok(())
89 }
90}