1use common_options::plugin_options::{PluginOptionsDeserializer, PluginOptionsSerializer};
16use serde::{Deserialize, Serialize};
17use serde_json::Value;
18
19#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
20pub struct DummyOptions;
21
22#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
23pub enum PluginOptions {
24 Dummy(DummyOptions),
25}
26
27pub struct PluginOptionsList(pub Vec<PluginOptions>);
28
29impl PluginOptionsSerializer for PluginOptionsList {
30 fn serialize(&self) -> Result<String, serde_json::Error> {
31 if self.0.is_empty() {
32 Ok(String::new())
33 } else {
34 serde_json::to_string(&self.0)
35 }
36 }
37}
38
39pub struct PluginOptionsDeserializerImpl;
40
41impl PluginOptionsDeserializer<Vec<PluginOptions>> for PluginOptionsDeserializerImpl {
42 fn deserialize(&self, payload: &str) -> Result<Vec<PluginOptions>, serde_json::Error> {
43 if payload.is_empty() {
44 Ok(vec![])
45 } else {
46 let values: Vec<Value> = serde_json::from_str(payload)?;
47 common_options::plugin_options::filter_known_plugin_options(values)
48 }
49 }
50}
51
52#[cfg(test)]
53mod tests {
54 use std::sync::{Mutex, OnceLock};
55
56 use super::*;
57
58 fn lock() -> std::sync::MutexGuard<'static, ()> {
60 static LOCK: OnceLock<Mutex<()>> = OnceLock::new();
61 LOCK.get_or_init(|| Mutex::new(())).lock().unwrap()
62 }
63
64 fn filter(values: Vec<Value>) -> Result<Vec<PluginOptions>, serde_json::Error> {
65 common_options::plugin_options::filter_known_plugin_options(values)
66 }
67
68 #[test]
69 fn test_drops_unknown_keeps_known() {
70 let _g = lock();
71 let payload = r#"[
73 {"Dummy": null},
74 {"SomeEnterpriseThing": {"foo": "bar", "num": 5}},
75 {"AnotherUnknown": {}}
76 ]"#;
77 let values: Vec<Value> = serde_json::from_str(payload).unwrap();
78 let kept = filter(values).unwrap();
79 assert_eq!(kept, vec![PluginOptions::Dummy(DummyOptions)]);
80 assert_eq!(
82 common_options::plugin_options::take_dropped_plugin_warnings(),
83 vec![
84 "SomeEnterpriseThing".to_string(),
85 "AnotherUnknown".to_string()
86 ]
87 );
88 }
89
90 #[test]
91 fn test_unknown_variant_tag_is_not_in_payload_warning() {
92 let _g = lock();
93 let values: Vec<Value> =
95 serde_json::from_str(r#"[{"SecretPlugin": {"token": "hunter2"}}]"#).unwrap();
96 filter(values).unwrap();
97 let recorded = common_options::plugin_options::take_dropped_plugin_warnings();
98 assert_eq!(recorded, vec!["SecretPlugin".to_string()]);
99 }
100
101 #[test]
102 fn test_known_variant_with_malformed_payload_errors() {
103 let _g = lock();
104 for bad in [r#"[{"Dummy": 1}]"#, r#"[{"Dummy": {"x": 1}}]"#] {
107 let values: Vec<Value> = serde_json::from_str(bad).unwrap();
108 let err = filter(values).unwrap_err();
109 assert!(
110 err.to_string().contains("Dummy"),
111 "expected the error to reference the Dummy variant, got: {err}"
112 );
113 }
114 assert!(common_options::plugin_options::take_dropped_plugin_warnings().is_empty());
116 }
117
118 #[test]
119 fn test_unrecognized_shape_errors() {
120 let _g = lock();
121 let values: Vec<Value> = serde_json::from_str(r#"[123]"#).unwrap();
123 assert!(filter(values).is_err());
124 }
125
126 #[test]
127 fn test_deserializer_impl_is_lenient_for_unknown() {
128 let _g = lock();
129 let payload = r#"[{"Dummy": null}, {"UnknownPlugin": {"x": 1}}]"#;
130 let kept = PluginOptionsDeserializerImpl.deserialize(payload).unwrap();
131 assert_eq!(kept, vec![PluginOptions::Dummy(DummyOptions)]);
132 common_options::plugin_options::take_dropped_plugin_warnings();
133 }
134
135 #[test]
136 fn test_deserializer_impl_errors_on_bad_known_payload() {
137 let _g = lock();
138 let payload = r#"[{"Dummy": 1}]"#;
139 assert!(PluginOptionsDeserializerImpl.deserialize(payload).is_err());
140 }
141}