1use std::{borrow::Cow, ops::BitOrAssign};
15
16use serde::{Deserialize, Serialize};
17use zenoh_keyexpr::keyexpr;
18use zenoh_result::ZResult;
19
20use crate::StructVersion;
21
22#[derive(Debug, Clone)]
26pub enum PluginDiff {
27 Delete(String),
28 Start(zenoh_config::PluginLoad),
29}
30
31#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord)]
36pub enum PluginState {
37 Declared,
38 Loaded,
39 Started,
40}
41
42#[derive(Copy, Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize, PartialOrd, Ord)]
47pub enum PluginReportLevel {
48 #[default]
49 Info,
50 Warning,
51 Error,
52}
53
54impl BitOrAssign for PluginReportLevel {
56 fn bitor_assign(&mut self, rhs: Self) {
57 if *self < rhs {
58 *self = rhs;
59 }
60 }
61}
62
63#[derive(Clone, Debug, PartialEq, Eq, Serialize, Default, Deserialize)]
66pub struct PluginReport {
67 level: PluginReportLevel,
68 #[serde(skip_serializing_if = "Vec::is_empty")]
69 messages: Vec<Cow<'static, str>>,
70}
71
72pub trait PluginStatus {
74 fn name(&self) -> &str;
76 fn id(&self) -> &str;
78 fn version(&self) -> Option<&str>;
80 fn long_version(&self) -> Option<&str>;
82 fn path(&self) -> &str;
84 fn state(&self) -> PluginState;
86 fn report(&self) -> PluginReport;
89}
90
91#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
93pub struct PluginStatusRec<'a> {
94 pub name: Cow<'a, str>,
95 pub id: Cow<'a, str>,
96 #[serde(skip_serializing_if = "Option::is_none")]
97 pub version: Option<Cow<'a, str>>,
98 pub long_version: Option<Cow<'a, str>>,
99 pub path: Cow<'a, str>,
100 pub state: PluginState,
101 pub report: PluginReport,
102}
103
104impl PluginStatus for PluginStatusRec<'_> {
105 fn name(&self) -> &str {
106 &self.name
107 }
108
109 fn id(&self) -> &str {
110 &self.id
111 }
112
113 fn version(&self) -> Option<&str> {
114 self.version.as_deref()
115 }
116 fn long_version(&self) -> Option<&str> {
117 self.long_version.as_deref()
118 }
119 fn path(&self) -> &str {
120 &self.path
121 }
122 fn state(&self) -> PluginState {
123 self.state
124 }
125 fn report(&self) -> PluginReport {
126 self.report.clone()
127 }
128}
129
130impl<'a> PluginStatusRec<'a> {
131 pub fn new<T: PluginStatus + ?Sized>(plugin: &'a T) -> Self {
133 Self {
134 name: Cow::Borrowed(plugin.name()),
135 id: Cow::Borrowed(plugin.id()),
136 version: plugin.version().map(Cow::Borrowed),
137 long_version: plugin.long_version().map(Cow::Borrowed),
138 path: Cow::Borrowed(plugin.path()),
139 state: plugin.state(),
140 report: plugin.report(),
141 }
142 }
143 pub fn into_owned(self) -> PluginStatusRec<'static> {
145 PluginStatusRec {
146 name: Cow::Owned(self.name.into_owned()),
147 id: Cow::Owned(self.id.into_owned()),
148 version: self.version.map(|v| Cow::Owned(v.into_owned())),
149 long_version: self.long_version.map(|v| Cow::Owned(v.into_owned())),
150 path: Cow::Owned(self.path.into_owned()),
151 state: self.state,
152 report: self.report,
153 }
154 }
155 pub(crate) fn prepend_name(self, prefix: &str) -> Self {
156 Self {
157 name: Cow::Owned(format!("{}/{}", prefix, self.name)),
158 ..self
159 }
160 }
161}
162
163pub trait PluginControl {
165 fn report(&self) -> PluginReport {
168 PluginReport::default()
169 }
170 fn plugins_status(&self, _names: &keyexpr) -> Vec<PluginStatusRec<'_>> {
173 Vec::new()
174 }
175}
176
177pub trait PluginStartArgs: StructVersion {}
178
179pub trait PluginInstance: PluginControl + Send + Sync {}
180
181pub trait Plugin: Sized + 'static {
183 type StartArgs: PluginStartArgs;
184 type Instance: PluginInstance;
185 const DEFAULT_NAME: &'static str;
187 const PLUGIN_VERSION: &'static str;
189 const PLUGIN_LONG_VERSION: &'static str;
191 fn start(name: &str, args: &Self::StartArgs) -> ZResult<Self::Instance>;
193}
194
195#[macro_export]
196macro_rules! plugin_version {
197 () => {
198 env!("CARGO_PKG_VERSION")
199 };
200}
201
202#[macro_export]
203macro_rules! plugin_long_version {
204 () => {
205 $crate::export::git_version::git_version!(prefix = "v", cargo_prefix = "v")
206 };
207}
208
209impl PluginReport {
210 pub fn new() -> Self {
211 Self::default()
212 }
213 pub fn clear(&mut self) {
214 *self = Self::default();
215 }
216 pub fn get_level(&self) -> PluginReportLevel {
217 self.level
218 }
219 pub fn add_error<S: Into<Cow<'static, str>>>(&mut self, error: S) {
220 self.level |= PluginReportLevel::Error;
221 self.messages.push(error.into());
222 }
223 pub fn add_warning<S: Into<Cow<'static, str>>>(&mut self, warning: S) {
224 self.level |= PluginReportLevel::Warning;
225 self.messages.push(warning.into());
226 }
227 pub fn add_info<S: Into<Cow<'static, str>>>(&mut self, message: S) {
228 self.level |= PluginReportLevel::Info;
229 self.messages.push(message.into());
230 }
231 pub fn messages(&self) -> &[Cow<'static, str>] {
232 &self.messages
233 }
234}
235
236pub trait PluginConditionSetter {
237 fn add_error(self, report: &mut PluginReport) -> Self;
238 fn add_warning(self, report: &mut PluginReport) -> Self;
239 fn add_info(self, report: &mut PluginReport) -> Self;
240}
241
242impl<T, E: ToString> PluginConditionSetter for core::result::Result<T, E> {
243 fn add_error(self, report: &mut PluginReport) -> Self {
244 if let Err(e) = &self {
245 report.add_error(e.to_string());
246 }
247 self
248 }
249 fn add_warning(self, report: &mut PluginReport) -> Self {
250 if let Err(e) = &self {
251 report.add_warning(e.to_string());
252 }
253 self
254 }
255 fn add_info(self, report: &mut PluginReport) -> Self {
256 if let Err(e) = &self {
257 report.add_info(e.to_string());
258 }
259 self
260 }
261}