summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Beyer <matthias.beyer@ifm.com>2022-05-13 09:35:37 +0200
committerMatthias Beyer <matthias.beyer@ifm.com>2022-05-13 09:35:37 +0200
commitd1b953a27576c370f756112f62783e9801a0b6b3 (patch)
tree60099113d432760985cb361f94c6e8b6447a2b3e
parent92607ccb7ad9984f841e4d13d10c978e44341d7e (diff)
parentc2cf5c4964d2298d2031b49728aadf8dd2f830ab (diff)
Merge branch 'feature/add_tedge_api/self-describing-configuration-types' into feature/add_tedge_api_impl
This merge brings in self-describing configuration type functionality, so that we can `tedge-cli doc <plugin_name>` for getting the documentation for the configuration of a plugin named `<plugin_name>`. Signed-off-by: Matthias Beyer <matthias.beyer@ifm.com>
-rw-r--r--Cargo.lock30
-rw-r--r--crates/core/tedge_api/Cargo.toml4
-rw-r--r--crates/core/tedge_api/examples/print_config.rs149
-rw-r--r--crates/core/tedge_api/src/config.rs196
-rw-r--r--crates/core/tedge_lib/Cargo.toml2
-rw-r--r--crates/core/tedge_lib/src/config.rs56
-rw-r--r--crates/core/tedge_lib/src/lib.rs1
-rw-r--r--plugins/plugin_avg/Cargo.toml1
-rw-r--r--plugins/plugin_avg/src/lib.rs13
-rw-r--r--plugins/plugin_httpstop/src/lib.rs7
-rw-r--r--plugins/plugin_inotify/src/config.rs5
-rw-r--r--plugins/plugin_inotify/src/lib.rs4
-rw-r--r--plugins/plugin_log/Cargo.toml1
-rw-r--r--plugins/plugin_log/src/lib.rs58
-rw-r--r--plugins/plugin_measurement_filter/Cargo.toml1
-rw-r--r--plugins/plugin_measurement_filter/src/builder.rs4
-rw-r--r--plugins/plugin_measurement_filter/src/config.rs7
-rw-r--r--plugins/plugin_measurement_filter/src/extractor.rs22
-rw-r--r--plugins/plugin_measurement_filter/src/filter.rs111
-rw-r--r--plugins/plugin_mqtt/src/builder.rs4
-rw-r--r--plugins/plugin_mqtt/src/config.rs24
-rw-r--r--plugins/plugin_mqtt_measurement_bridge/src/lib.rs9
-rw-r--r--plugins/plugin_notification/src/builder.rs4
-rw-r--r--plugins/plugin_notification/src/config.rs37
-rw-r--r--tedge/Cargo.toml5
-rw-r--r--tedge/src/cli.rs7
-rw-r--r--tedge/src/config.rs173
-rw-r--r--tedge/src/main.rs145
28 files changed, 689 insertions, 391 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 7eb903db..cd02f8da 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1373,6 +1373,12 @@ dependencies = [
]
[[package]]
+name = "indoc"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05a0bd019339e5d968b37855180087b7b9d512c5046fbd244cf8c95687927d6e"
+
+[[package]]
name = "inotify"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2186,7 +2192,6 @@ name = "plugin_avg"
version = "0.1.0"
dependencies = [
"async-trait",
- "humantime-serde",
"miette",
"serde",
"tedge_api",
@@ -2239,6 +2244,7 @@ name = "plugin_log"
version = "0.1.0"
dependencies = [
"async-trait",
+ "indoc",
"log",
"miette",
"serde",
@@ -2254,6 +2260,7 @@ name = "plugin_measurement_filter"
version = "0.1.0"
dependencies = [
"async-trait",
+ "indoc",
"miette",
"serde",
"serde_with",
@@ -3317,6 +3324,7 @@ dependencies = [
"clap 3.1.6",
"env_logger 0.9.0",
"miette",
+ "nu-ansi-term",
"plugin_avg",
"plugin_httpstop",
"plugin_inotify",
@@ -3326,9 +3334,13 @@ dependencies = [
"plugin_mqtt_measurement_bridge",
"plugin_notification",
"plugin_sysstat",
+ "pretty",
"tedge_api",
"tedge_core",
"tedge_lib",
+ "term_size",
+ "termcolor",
+ "termimad",
"tokio",
"toml",
"tracing",
@@ -3388,14 +3400,10 @@ dependencies = [
"downcast-rs",
"futures",
"miette",
- "nu-ansi-term",
- "pretty",
"pretty_assertions",
"serde",
"static_assertions",
"tedge_config_derive",
- "termcolor",
- "termimad",
"thiserror",
"tokio",
"tokio-util 0.7.0",
@@ -3478,6 +3486,8 @@ version = "0.1.0"
dependencies = [
"async-trait",
"futures",
+ "humantime-serde",
+ "indoc",
"log",
"serde",
"tedge_api",
@@ -3563,6 +3573,16 @@ dependencies = [
]
[[package]]
+name = "term_size"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
name = "termcolor"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/crates/core/tedge_api/Cargo.toml b/crates/core/tedge_api/Cargo.toml
index 619e769e..2f316c2f 100644
--- a/crates/core/tedge_api/Cargo.toml
+++ b/crates/core/tedge_api/Cargo.toml
@@ -15,10 +15,6 @@ tokio = { version = "1.16.1", features = ["sync", "time"] }
tokio-util = "0.7.0"
toml = "0.5.8"
serde = { version = "1.0.136", features = ["derive"] }
-pretty = { version = "0.11.3", features = ["termcolor"] }
-termcolor = "1.1.3"
-termimad = "0.20.1"
-nu-ansi-term = "0.45.1"
tedge_config_derive = { version = "0.1.0", path = "tedge_config_derive" }
[dev-dependencies]
diff --git a/crates/core/tedge_api/examples/print_config.rs b/crates/core/tedge_api/examples/print_config.rs
deleted file mode 100644
index ba6e7a6b..00000000
--- a/crates/core/tedge_api/examples/print_config.rs
+++ /dev/null
@@ -1,149 +0,0 @@
-use std::collections::HashMap;
-
-use nu_ansi_term::Color;
-use pretty::Arena;
-use tedge_api::{
- config::{AsConfig, ConfigDescription, ConfigKind},
- Config,
-};
-struct Port(u64);
-
-impl AsConfig for Port {
- fn as_config() -> ConfigDescription {
- ConfigDescription::new(
- String::from("Integer"),
- ConfigKind::Integer,
- Some("A TCP port number is an integer between 0 and 65535"),
- )
- }
-}
-
-struct VHost;
-
-impl AsConfig for VHost {
- fn as_config() -> ConfigDescription {
- ConfigDescription::new(
- String::from("VHost"),
- ConfigKind::Struct(vec![("name", None, String::as_config())]),
- Some("A virtual host definition"),
- )
- }
-}
-
-fn main() {
- let arena = Arena::new();
-
- let doc = Vec::<String>::as_config();
- let rendered_doc = doc.as_terminal_doc(&arena);
-
- let mut output = String::new();
-
- rendered_doc.render_fmt(80, &mut output).unwrap();
-
- println!(
- "------- Output for {}",
- std::any::type_name::<Vec<String>>()
- );
- println!("{}", output);
-
- let arena = Arena::new();
-
- let doc = ConfigDescription::new(
- String::from("ServerConfig"),
- ConfigKind::Struct(vec![
- ("port", None, Port::as_config()),
- ("interface", None, String::as_config()),
- ("virtual_hosts", None, Vec::<VHost>::as_config()),
- ("headers", None, HashMap::<String, String>::as_config()),
- ]),
- Some("Specify how the server should be started\n\n## Note\n\nThis is a reallly really loooooooooooooooooong loooooooooooooooooooong new *line*."),
- );
- let rendered_doc = doc.as_terminal_doc(&arena);
-
- let mut output = String::new();
-
- rendered_doc.render_fmt(80, &mut output).unwrap();
-
- println!(
- "Configuration for {} plugin kinds",
- Color::White.bold().paint(doc.name())
- );
- println!(
- "{}",
- Color::White.dimmed().bold().paint(format!(
- "=================={}=============",
- std::iter::repeat('=')
- .take(doc.name().len())
- .collect::<String>()
- ))
- );
- println!("------- Output for ServerConfig");
- println!("{}", output);
- let arena = Arena::new();
-
- #[derive(Config)]
- #[config(tag = "type")]
- /// An Nginx virtual host
- ///
- /// # Note
- ///
- /// This is an example and as such is nonsense
- enum NginxVHost {
- /// A simple host consisting of a string
- Simple(String),
- /// A more complex host that can also specify its port
- Complex {
- /// the name of the VHost
- name: String,
- port: Port,
- },
- UndocumentedComplex {
- num: u16,
- foo: f32,
- },
- }
-
- #[derive(Config)]
- #[config(untagged)]
- enum DebugLevel {
- /// Enables debug output
- ///
- /// And info of course
- Debug,
- /// Only pertinent information will be logged
- Info,
- /// A custom debug level
- Custom(String),
- }
-
- #[derive(Config)]
- struct NginxConfig {
- vhosts: Vec<NginxVHost>,
- debug_level: DebugLevel,
- allow_priv_ports: bool,
- }
-
- let doc = NginxConfig::as_config();
- let rendered_doc = doc.as_terminal_doc(&arena);
-
- let mut output = String::new();
-
- rendered_doc.render_fmt(80, &mut output).unwrap();
-
- println!("------- Output for NginxConfig");
- println!(
- "Configuration for {} plugin kinds",
- Color::White.bold().paint(doc.name())
- );
- println!(
- "{}",
- Color::White.dimmed().bold().paint(format!(
- "=================={}=============",
- std::iter::repeat('=')
- .take(doc.name().len())
- .collect::<String>()
- ))
- );
- println!("{}", output);
- println!("-------");
-}
diff --git a/crates/core/tedge_api/src/config.rs b/crates/core/tedge_api/src/config.rs
index 90bee595..ff25e1b4 100644
--- a/crates/core/tedge_api/src/config.rs
+++ b/crates/core/tedge_api/src/config.rs
@@ -1,9 +1,6 @@
use std::collections::HashMap;
-use nu_ansi_term::Color;
-use pretty::{Arena, Doc, DocAllocator, Pretty, RefDoc};
use serde::Serialize;
-use termimad::MadSkin;
/// Generic config that represents what kind of config a plugin wishes to accept
#[derive(Debug, Serialize, PartialEq)]
@@ -131,6 +128,16 @@ pub trait AsConfig {
fn as_config() -> ConfigDescription;
}
+impl<T: AsConfig> AsConfig for Option<T> {
+ fn as_config() -> ConfigDescription {
+ ConfigDescription::new(
+ format!("An optional '{}'", T::as_config().name()),
+ ConfigKind::Wrapped(Box::new(T::as_config())),
+ None,
+ )
+ }
+}
+
impl<T: AsConfig> AsConfig for Vec<T> {
fn as_config() -> ConfigDescription {
ConfigDescription::new(
@@ -151,6 +158,16 @@ impl<V: AsConfig> AsConfig for HashMap<String, V> {
}
}
+impl<V: AsConfig> AsConfig for HashMap<std::path::PathBuf, V> {
+ fn as_config() -> ConfigDescription {
+ ConfigDescription::new(
+ format!("Table of '{}'s", V::as_config().name()),
+ ConfigKind::HashMap(Box::new(V::as_config())),
+ None,
+ )
+ }
+}
+
macro_rules! impl_config_kind {
($kind:expr; $name:expr; $doc:expr => $($typ:ty),+) => {
$(
@@ -181,176 +198,9 @@ impl_config_kind!(ConfigKind::Float; "Float"; "A floating point value with 32 bi
impl_config_kind!(ConfigKind::Bool; "Boolean"; "A boolean" => bool);
impl_config_kind!(ConfigKind::String; "String"; "An UTF-8 string" => String);
-/******Pretty Printing of Configs******/
-
-impl ConfigDescription {
- /// Get a [`RcDoc`](pretty::RcDoc) which can be used to write the documentation of this
- pub fn as_terminal_doc<'a>(&'a self, arena: &'a Arena<'a>) -> RefDoc<'a> {
- let mut doc = arena.nil();
-
- if !matches!(self.kind(), ConfigKind::Wrapped(_)) && self.doc().is_none() {
- doc = doc
- .append(Color::LightBlue.bold().paint(self.name()).to_string())
- .append(arena.space())
- .append(match self.kind() {
- ConfigKind::Bool
- | ConfigKind::Integer
- | ConfigKind::Float
- | ConfigKind::String
- | ConfigKind::Wrapped(_)
- | ConfigKind::Array(_)
- | ConfigKind::HashMap(_) => arena.nil(),
- ConfigKind::Struct(_) => {
- arena.text(Color::Blue.dimmed().paint("[Table]").to_string())
- }
- ConfigKind::Enum(_, _) => {
- arena.text(Color::Green.dimmed().paint("[Enum]").to_string())
- }
- })
- .append(arena.hardline());
- }
-
- let skin = MadSkin::default_dark();
- let render_markdown = |text: &str| {
- let rendered = skin.text(text, None).to_string();
- arena.intersperse(
- rendered.split("\n").map(|t| {
- arena.intersperse(
- t.split(char::is_whitespace).map(|t| t.to_string()),
- arena.softline(),
- )
- }),
- arena.hardline(),
- )
- };
-
- if let Some(conf_doc) = self.doc() {
- doc = doc.append(render_markdown(&conf_doc));
- }
-
- match self.kind() {
- ConfigKind::Bool | ConfigKind::Integer | ConfigKind::Float | ConfigKind::String => (),
- ConfigKind::Struct(stc) => {
- doc = doc
- .append(arena.hardline())
- .append(Color::Blue.paint("[Members]").to_string())
- .append(arena.hardline())
- .append(arena.intersperse(
- stc.iter().map(|(member_name, member_doc, member_conf)| {
- let mut doc = arena.nil();
-
- if let Some(member_doc) = member_doc {
- doc = doc.append(render_markdown(&member_doc));
- }
- doc.append(
- arena.text(Color::Blue.bold().paint(*member_name).to_string()),
- )
- .append(": ")
- .append(
- Pretty::pretty(member_conf.as_terminal_doc(arena), arena).nest(4),
- )
- }),
- Doc::hardline(),
- ))
- }
- ConfigKind::Enum(enum_kind, variants) => {
- doc = doc
- .append(arena.hardline())
- .append(Color::Green.paint("One of:").to_string())
- .append(arena.space())
- .append(match enum_kind {
- ConfigEnumKind::Tagged(tag) => arena.text(
- Color::White
- .dimmed()
- .paint(format!(
- "[Tagged with {}]",
- Color::LightGreen
- .italic()
- .dimmed()
- .paint(format!("'{}'", tag))
- ))
- .to_string(),
- ),
- ConfigEnumKind::Untagged => {
- arena.text(Color::White.dimmed().paint("[Untagged]").to_string())
- }
- })
- .append(arena.hardline())
- .append(
- arena.intersperse(
- variants
- .iter()
- .map(|(member_name, member_doc, member_conf)| {
- arena.text("-").append(arena.space()).append({
- let mut doc = arena
- .nil()
- .append(match member_conf {
- EnumVariantRepresentation::String(_) => arena.text(
- Color::Green
- .bold()
- .paint(&format!(
- "{:?}",
- member_name.to_lowercase()
- ))
- .to_string(),
- ),
- EnumVariantRepresentation::Wrapped(_) => arena
- .text(
- Color::Green
- .bold()
- .paint(*member_name)
- .to_string(),
- ),
- })
- .append(": ");
-
- if let Some(member_doc) = member_doc {
- doc = doc.append(render_markdown(&member_doc));
- }
-
- doc.append(
- Pretty::pretty(
- match member_conf {
- EnumVariantRepresentation::String(_) => {
- arena.nil().into_doc()
- }
-
- EnumVariantRepresentation::Wrapped(
- member_conf,
- ) => arena
- .text(
- Color::LightRed
- .paint("Is a: ")
- .to_string(),
- )
- .append(member_conf.as_terminal_doc(arena))
- .into_doc(),
- },
- arena,
- )
- .nest(4),
- )
- .nest(2)
- })
- }),
- Doc::hardline(),
- ),
- );
- }
- ConfigKind::Array(conf) => {
- doc = doc
- .append(Color::LightRed.paint("Many of:").to_string())
- .append(arena.space())
- .append(conf.as_terminal_doc(arena));
- }
- ConfigKind::HashMap(conf) | ConfigKind::Wrapped(conf) => {
- doc = doc.append(conf.as_terminal_doc(arena));
- }
- };
-
- doc.into_doc()
- }
-}
+impl_config_kind!(ConfigKind::String; "String"; "A socket address" => std::net::SocketAddr);
+impl_config_kind!(ConfigKind::String; "String"; "An IPv4 socket address" => std::net::SocketAddrV4);
+impl_config_kind!(ConfigKind::String; "String"; "An IPv6 socket address" => std::net::SocketAddrV6);
#[cfg(test)]
mod tests {
diff --git a/crates/core/tedge_lib/Cargo.toml b/crates/core/tedge_lib/Cargo.toml
index 083779ea..5c95662a 100644
--- a/crates/core/tedge_lib/Cargo.toml
+++ b/crates/core/tedge_lib/Cargo.toml
@@ -14,5 +14,7 @@ thiserror = "1.0.30"
tokio = { version = "1.16.1", features = ["macros", "rt", "sync", "time"] }
toml = "0.5.8"
tracing = "0.1"
+humantime-serde = "1"
+indoc = "1.0.6"
tedge_api = { path = "../tedge_api" }
diff --git a/crates/core/tedge_lib/src/config.rs b/crates/core/tedge_lib/src/config.rs
new file mode 100644
index 00000000..b06ef3ff
--- /dev/null
+++ b/crates/core/tedge_lib/src/config.rs
@@ -0,0 +1,56 @@
+#[derive(Copy, Clone, Debug, serde::Deserialize)]
+#[serde(transparent)]
+pub struct Humantime(#[serde(with = "humantime_serde")] std::time::Duration);
+
+impl Humantime {
+ pub fn into_duration(self) -> std::time::Duration {
+ self.0
+ }
+}
+
+impl tedge_api::AsConfig for Humantime {
+ fn as_config() -> tedge_api::ConfigDescription {
+ tedge_api::ConfigDescription::new(
+ "Duration-representing String".to_string(),
+ tedge_api::ConfigKind::String,
+ Some(indoc::indoc!{r#"
+ A String that represents a duration
+
+ ## Examples
+
+ A duration of one minute:
+
+ ```toml
+ "1min"
+ ```
+
+ A duration of 5 minutes and 2 nanoseconds:
+
+ ```toml
+ "5min 2ns"
+ ```
+
+ ## More information
+
+ For more information have a look at the documentation of
+ [the humantime crate](https://docs.rs/humantime).
+ "#}),
+ )
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ #[test]
+ fn test_humantime_deser() {
+ let ts = r#"t = "1sec""#;
+
+ #[derive(serde::Deserialize)]
+ struct T {
+ t: super::Humantime,
+ }
+
+ let ht: T = toml::from_str(ts).unwrap();
+ assert_eq!(ht.t.0, std::time::Duration::from_secs(1));
+ }
+}
diff --git a/crates/core/tedge_lib/src/lib.rs b/crates/core/tedge_lib/src/lib.rs
index 279aad2b..4f52bd8a 100644
--- a/crates/core/tedge_lib/src/lib.rs
+++ b/crates/core/tedge_lib/src/lib.rs
@@ -1,3 +1,4 @@
+pub mod config;
pub mod iter;
pub mod mainloop;
pub mod measurement;
diff --git a/plugins/plugin_avg/Cargo.toml b/plugins/plugin_avg/Cargo.toml
index 0d1cc156..db7884ab 100644
--- a/plugins/plugin_avg/Cargo.toml
+++ b/plugins/plugin_avg/Cargo.toml
@@ -7,7 +7,6 @@ edition = "2021"
[dependencies]
async-trait = "0.1"
-humantime-serde = "1"
miette = "4.4"
serde = { version = "1.0.136", features = ["derive"] }
thiserror = "1"
diff --git a/plugins/plugin_avg/src/lib.rs b/plugins/plugin_avg/src/lib.rs
index 47ff0aa4..45202c9b 100644
--- a/plugins/plugin_avg/src/lib.rs
+++ b/plugins/plugin_avg/src/lib.rs
@@ -21,11 +21,12 @@ use tracing::Instrument;
pub struct AvgPluginBuilder;
-#[derive(serde::Deserialize, Debug)]
+#[derive(serde::Deserialize, Debug, tedge_api::Config)]
struct AvgConfig {
- #[serde(with = "humantime_serde")]
- timeframe: std::time::Duration,
+ /// The duration of the time window to calculate the average for
+ timeframe: tedge_lib::config::Humantime,
+ /// The name of the plugin to send the result to
target: String,
/// Whether to report a 0 (zero) if there are zero measurements in the timeframe
@@ -47,6 +48,10 @@ impl<PD: PluginDirectory> PluginBuilder<PD> for AvgPluginBuilder {
"avg"
}
+ fn kind_configuration() -> Option<tedge_api::ConfigDescription> {
+ Some(<AvgConfig as tedge_api::AsConfig>::as_config())
+ }
+
async fn verify_configuration(
&self,
config: &PluginConfiguration,
@@ -121,7 +126,7 @@ impl Plugin for AvgPlugin {
values: self.values.clone(),
};
let (stopper, mainloop) =
- tedge_lib::mainloop::Mainloop::ticking_every(self.config.timeframe, state);
+ tedge_lib::mainloop::Mainloop::ticking_every(self.config.timeframe.into_duration(), state);
self.stopper = Some(stopper);
let _ = tokio::spawn(
diff --git a/plugins/plugin_httpstop/src/lib.rs b/plugins/plugin_httpstop/src/lib.rs
index 24eacc70..c109f826 100644
--- a/plugins/plugin_httpstop/src/lib.rs
+++ b/plugins/plugin_httpstop/src/lib.rs
@@ -9,8 +9,9 @@ use tokio::task::JoinHandle;
use tokio_util::sync::CancellationToken;
use tracing::{debug, error, Instrument};
-#[derive(serde::Deserialize, Debug)]
+#[derive(serde::Deserialize, Debug, tedge_api::Config)]
struct HttpStopConfig {
+ /// The address to listen on
bind: std::net::SocketAddr,
}
@@ -37,6 +38,10 @@ where
"httpstop"
}
+ fn kind_configuration() -> Option<tedge_api::ConfigDescription> {
+ Some(<HttpStopConfig as tedge_api::AsConfig>::as_config())
+ }
+
fn kind_message_types() -> HandleTypes
where
Self: Sized,
diff --git a/plugins/plugin_inotify/src/config.rs b/plugins/plugin_inotify/src/config.rs
index 0b201687..6859b4e9 100644
--- a/plugins/plugin_inotify/src/config.rs
+++ b/plugins/plugin_inotify/src/config.rs
@@ -1,7 +1,7 @@
use std::collections::HashMap;
use std::path::PathBuf;
-#[derive(serde::Deserialize, Debug)]
+#[derive(serde::Deserialize, Debug, tedge_api::Config)]
pub struct InotifyConfig {
/// Target to send notifications to
pub(crate) target: String,
@@ -21,7 +21,8 @@ fn fail_on_err_default() -> bool {
true
}
-#[derive(serde::Deserialize, Copy, Clone, Debug)]
+#[derive(serde::Deserialize, Copy, Clone, Debug, tedge_api::Config)]
+#[config(untagged)]
#[allow(non_camel_case_types)]
pub enum Watchmode {
/// File was accessed
diff --git a/plugins/plugin_inotify/src/lib.rs b/plugins/plugin_inotify/src/lib.rs
index 68687b0b..2b1800d7 100644
--- a/plugins/plugin_inotify/src/lib.rs
+++ b/plugins/plugin_inotify/src/lib.rs
@@ -43,6 +43,10 @@ impl<PD: PluginDirectory> PluginBuilder<PD> for InotifyPluginBuilder {
"inotify"
}
+ fn kind_configuration() -> Option<tedge_api::ConfigDescription> {
+ Some(<InotifyConfig as tedge_api::AsConfig>::as_config())
+ }
+
fn kind_message_types() -> HandleTypes
where
Self: Size