diff options
author | Matthias Beyer <matthias.beyer@ifm.com> | 2022-07-03 11:28:15 +0200 |
---|---|---|
committer | Matthias Beyer <matthias.beyer@ifm.com> | 2022-07-03 11:28:15 +0200 |
commit | 9fe671af827a99f2ee5bd59ba21e0824fc935b24 (patch) | |
tree | cc2dd2482ac1093b43855726a739557092ac35c9 /tedge | |
parent | 5ed9a35bde99763a88c40758952d23ce5a311100 (diff) | |
parent | 36553ba01a959d012e248bf27527a0041da860e9 (diff) |
Merge branch 'feature/add_tedge_api/cli-as-lib-and-bin' into feature/add_tedge_api_impl
Diffstat (limited to 'tedge')
-rw-r--r-- | tedge/Cargo.toml | 8 | ||||
-rw-r--r-- | tedge/src/cli.rs | 14 | ||||
-rw-r--r-- | tedge/src/lib.rs | 103 | ||||
-rw-r--r-- | tedge/src/logging.rs | 2 | ||||
-rw-r--r-- | tedge/src/main.rs | 302 | ||||
-rw-r--r-- | tedge/src/registry.rs | 78 |
6 files changed, 281 insertions, 226 deletions
diff --git a/tedge/Cargo.toml b/tedge/Cargo.toml index 533526af..9e81f7cd 100644 --- a/tedge/Cargo.toml +++ b/tedge/Cargo.toml @@ -5,6 +5,14 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +name = "tedge_cli" +path = "src/lib.rs" + +[[bin]] +name = "tedge-cli" +path = "src/main.rs" + [dependencies] clap = { version = "3", features = ["derive", "cargo", "suggestions"] } toml = "0.5.8" diff --git a/tedge/src/cli.rs b/tedge/src/cli.rs index 821b1cd9..e01bc313 100644 --- a/tedge/src/cli.rs +++ b/tedge/src/cli.rs @@ -6,7 +6,7 @@ use std::path::PathBuf; version = clap::crate_version!(), about = clap::crate_description!() )] -pub(crate) struct Cli { +pub struct Cli { /// Enable logging /// /// If not specified, only WARN and ERROR messages will be logged. @@ -14,26 +14,26 @@ pub(crate) struct Cli { /// /// This setting overwrites the logging specification set via the RUST_LOG env variable. #[clap(short, long, arg_enum)] - pub(crate) logging: Option<LoggingSpec>, + pub logging: Option<LoggingSpec>, /// Enable chrome compatible tracing output /// /// If set, chrome-compatible tracing output will be written to the file specified. #[clap(long)] - pub(crate) chrome_logging: Option<PathBuf>, + pub chrome_logging: Option<PathBuf>, /// Enable tracy compatible tracing output /// /// If set, "tracy" compatible tracing output will be produced #[clap(long)] - pub(crate) tracy_logging: bool, + pub tracy_logging: bool, #[clap(subcommand)] - pub(crate) command: CliCommand, + pub command: CliCommand, } #[derive(Copy, Clone, Debug, clap::ArgEnum)] -pub(crate) enum LoggingSpec { +pub enum LoggingSpec { False, Off, Trace, @@ -44,7 +44,7 @@ pub(crate) enum LoggingSpec { } #[derive(Debug, clap::Subcommand)] -pub(crate) enum CliCommand { +pub enum CliCommand { /// Run thin-edge with the passed configuration #[clap(name = "run")] Run { config: PathBuf }, diff --git a/tedge/src/lib.rs b/tedge/src/lib.rs new file mode 100644 index 00000000..69cc0b3e --- /dev/null +++ b/tedge/src/lib.rs @@ -0,0 +1,103 @@ +#![doc = include_str!("../README.md")] + +use miette::IntoDiagnostic; +use tracing::debug; +use tracing::error; +use tracing::info; + +use tedge_core::TedgeApplication; +use tedge_core::TedgeApplicationCancelSender; + +pub mod cli; +pub mod config; +pub mod logging; +mod registry; + +pub use crate::registry::Registry; + +pub async fn run_app(args: crate::cli::Cli, registry: Registry) -> miette::Result<()> { + match args.command { + cli::CliCommand::Run { config } => { + let (cancel_sender, application) = + registry.app_builder.with_config_from_path(config).await?; + info!("Application built"); + + debug!("Verifying the configuration"); + application.verify_configurations().await?; + + debug!("Going to run the application"); + run(cancel_sender, application).await + } + cli::CliCommand::ValidateConfig { config } => { + let (_, application) = registry.app_builder.with_config_from_path(config).await?; + info!("Application built"); + + debug!("Only going to validate the configuration"); + application.verify_configurations().await?; + info!("Configuration validated"); + Ok(()) + } + cli::CliCommand::GetPluginKinds => { + use std::io::Write; + + let mut out = std::io::stdout(); + for name in registry.app_builder.plugin_kind_names() { + writeln!(out, "{}", name).into_diagnostic()?; + } + Ok(()) + } + cli::CliCommand::Doc { plugin_name } => { + let mut registry = registry; + if let Some(plugin_name) = plugin_name { + let printer = registry + .doc_printers + .remove(&plugin_name) + .ok_or_else(|| miette::miette!("Plugin named '{}' not found", plugin_name))?; + + (printer)()?; + } else { + for printer in registry.doc_printers.into_values() { + printer()?; + } + } + + Ok(()) + } + } +} + +async fn run( + cancel_sender: TedgeApplicationCancelSender, + application: TedgeApplication, +) -> miette::Result<()> { + info!("Booting app now."); + let mut run_fut = Box::pin(application.run()); + + let kill_app = |fut| -> miette::Result<()> { + error!("Killing application"); + drop(fut); + miette::bail!("Application killed") + }; + + let res = tokio::select! { + res = &mut run_fut => { + res + }, + + _int = tokio::signal::ctrl_c() => { + if !cancel_sender.is_cancelled() { + info!("Shutting down..."); + cancel_sender.cancel_app(); + tokio::select! { + res = &mut run_fut => res, + _ = tokio::signal::ctrl_c() => return kill_app(run_fut), + } + } else { + return kill_app(run_fut); + } + }, + }; + + info!("Bye"); + Ok(res?) +} diff --git a/tedge/src/logging.rs b/tedge/src/logging.rs index 0be2a1fa..3b4fd837 100644 --- a/tedge/src/logging.rs +++ b/tedge/src/logging.rs @@ -7,7 +7,7 @@ use tracing_subscriber::Layer; use crate::cli::LoggingSpec; -pub(crate) fn setup_logging( +pub fn setup_logging( spec: Option<LoggingSpec>, chrome_logging: Option<&PathBuf>, tracy_logging: bool, diff --git a/tedge/src/main.rs b/tedge/src/main.rs index 6800c45a..58b4ef68 100644 --- a/tedge/src/main.rs +++ b/tedge/src/main.rs @@ -1,87 +1,16 @@ -#![doc = include_str!("../README.md")] - -use std::collections::HashMap; -use std::collections::HashSet; -use std::io::Write; - use clap::Parser; -use miette::IntoDiagnostic; - -use pretty::Arena; -use tedge_api::PluginBuilder; -use tedge_core::TedgeApplication; -use tedge_core::TedgeApplicationBuilder; -use tedge_core::TedgeApplicationCancelSender; -use tedge_lib::measurement::Measurement; -use tedge_lib::notification::Notification; use tracing::debug; -use tracing::error; use tracing::info; -mod cli; -mod config; -mod logging; - -/// Helper type for registering PluginBuilder instances and doc-printing functions -struct Registry { - app_builder: TedgeApplicationBuilder, - plugin_kinds: HashSet<String>, - doc_printers: HashMap<String, Box<dyn FnOnce() -> Result<(), miette::Error>>>, -} - -macro_rules! register_plugin { - ($registry:ident, $cfg:tt, $pluginbuilder:ty, $pbinstance:expr) => {{ - cfg_if::cfg_if! { - if #[cfg(feature = $cfg)] { - let kind_name: &'static str = <$pluginbuilder as PluginBuilder<tedge_core::PluginDirectory>>::kind_name(); - info!(%kind_name, "Registering plugin builder"); - let mut registry = $registry; - if !registry.plugin_kinds.insert(kind_name.to_string()) { - miette::bail!("Plugin kind '{}' was already registered, cannot register!", kind_name) - } - - let kind_name_str = kind_name.to_string(); - registry.doc_printers.insert(kind_name.to_string(), Box::new(move || { - let mut stdout = std::io::stdout(); - if let Some(config_desc) = <$pluginbuilder as PluginBuilder<tedge_core::PluginDirectory>>::kind_configuration() { - let terminal_width = term_size::dimensions().map(|(w, _)| w).unwrap_or(80); - let arena = Arena::new(); - - let rendered_doc = crate::config::as_terminal_doc(&config_desc, &arena); - - let mut output = String::new(); - rendered_doc.render_fmt(terminal_width, &mut output).into_diagnostic()?; - - writeln!(stdout, " ----- Documentation for plugin '{}'", kind_name_str) - .into_diagnostic()?; - - writeln!(stdout, "{}", output).into_diagnostic()?; - } else { - let msg = format!(" Documentation for plugin '{}' is unavailable", kind_name); - writeln!(stdout, "{}", nu_ansi_term::Color::Red.bold().paint(msg)) - .into_diagnostic()?; - } - Ok(()) - })); - - Registry { - app_builder: registry.app_builder.with_plugin_builder($pbinstance), - plugin_kinds: registry.plugin_kinds, - doc_printers: registry.doc_printers, - } - } else { - tracing::trace!("Not supporting plugins of type {}", std::stringify!($pluginbuilder)); - $registry - } - } - }} -} +use tedge_cli::Registry; +use tedge_lib::measurement::Measurement; +use tedge_lib::notification::Notification; #[tokio::main] #[tracing::instrument] async fn main() -> miette::Result<()> { - let args = crate::cli::Cli::parse(); - let _guard = crate::logging::setup_logging( + let args = tedge_cli::cli::Cli::parse(); + let _guard = tedge_cli::logging::setup_logging( args.logging, args.chrome_logging.as_ref(), args.tracy_logging, @@ -89,162 +18,99 @@ async fn main() -> miette::Result<()> { info!("Tedge booting..."); debug!(?args, "Tedge CLI"); - let registry = Registry { - app_builder: TedgeApplication::builder(), - plugin_kinds: HashSet::new(), - doc_printers: HashMap::new(), - }; + let registry = tedge_cli::Registry::new(); info!("Building application"); let registry = { cfg_table::cfg_table! { - [not(feature = "mqtt")] => register_plugin!( - registry, - "builtin_plugin_log", - plugin_log::LogPluginBuilder<(Measurement, Notification)>, - plugin_log::LogPluginBuilder::<(Measurement, Notification)>::new() - ), - - [feature = "mqtt"] => register_plugin!( - registry, - "builtin_plugin_log", - plugin_log::LogPluginBuilder<(Measurement, Notification, plugin_mqtt::IncomingMessage)>, - plugin_log::LogPluginBuilder::<(Measurement, Notification, plugin_mqtt::IncomingMessage)>::new() - ), + [not(feature = "mqtt")] => { + tedge_cli::register_plugin!( + if feature "builtin_plugin_log" is enabled then + register on registry + builder of type plugin_log::LogPluginBuilder<(Measurement, Notification)>, + with instance { + plugin_log::LogPluginBuilder::<(Measurement, Notification)>::new() + } + ) + }, + + [feature = "mqtt"] => { + tedge_cli::register_plugin!( + if feature "builtin_plugin_log" is enabled then + register on registry + builder of type plugin_log::LogPluginBuilder<(Measurement, Notification, plugin_mqtt::IncomingMessage)>, + with instance { + plugin_log::LogPluginBuilder::<(Measurement, Notification, plugin_mqtt::IncomingMessage)>::new() + } + ) + }, } }; - let registry = register_plugin!( - registry, - "builtin_plugin_avg", - plugin_avg::AvgPluginBuilder, - plugin_avg::AvgPluginBuilder - ); - let registry = register_plugin!( - registry, - "builtin_plugin_sysstat", - plugin_sysstat::SysStatPluginBuilder, - plugin_sysstat::SysStatPluginBuilder - ); - let registry = register_plugin!( - registry, - "builtin_plugin_inotify", - plugin_inotify::InotifyPluginBuilder, - plugin_inotify::InotifyPluginBuilder - ); - let registry = register_plugin!( - registry, - "builtin_plugin_httpstop", - plugin_httpstop::HttpStopPluginBuilder, - plugin_httpstop::HttpStopPluginBuilder - ); - let registry = register_plugin!( - registry, - "builtin_plugin_measurement_filter", - plugin_measurement_filter::MeasurementFilterPluginBuilder, - plugin_measurement_filter::MeasurementFilterPluginBuilder + let registry = tedge_cli::register_plugin!( + if feature "builtin_plugin_avg" is enabled then + register on registry + builder of type plugin_avg::AvgPluginBuilder, + with instance { + plugin_avg::AvgPluginBuilder + } ); - let registry = register_plugin!( - registry, - "mqtt", - plugin_mqtt::MqttPluginBuilder, - plugin_mqtt::MqttPluginBuilder::new() + let registry = tedge_cli::register_plugin!( + if feature "builtin_plugin_sysstat" is enabled then + register on registry + builder of type plugin_sysstat::SysStatPluginBuilder, + with instance { + plugin_sysstat::SysStatPluginBuilder + } ); - let registry = register_plugin!( - registry, - "mqtt", - plugin_mqtt_measurement_bridge::MqttMeasurementBridgePluginBuilder, - plugin_mqtt_measurement_bridge::MqttMeasurementBridgePluginBuilder::new() + let registry = tedge_cli::register_plugin!( + if feature "builtin_plugin_inotify" is enabled then + register on registry + builder of type plugin_inotify::InotifyPluginBuilder, + with instance { + plugin_inotify::InotifyPluginBuilder + } ); - let registry = register_plugin!( - registry, - "builtin_plugin_notification", - plugin_notification::NotificationPluginBuilder, - plugin_notification::NotificationPluginBuilder + let registry = tedge_cli::register_plugin!( + if feature "builtin_plugin_httpstop" is enabled then + register on registry + builder of type plugin_httpstop::HttpStopPluginBuilder, + with instance { + plugin_httpstop::HttpStopPluginBuilder + } ); - - match args.command { - cli::CliCommand::Run { config } => { - let (cancel_sender, application) = - registry.app_builder.with_config_from_path(config).await?; - info!("Application built"); - - debug!("Verifying the configuration"); - application.verify_configurations().await?; - - debug!("Going to run the application"); - run(cancel_sender, application).await + let registry = tedge_cli::register_plugin!( + if feature "builtin_plugin_measurement_filter" is enabled then + register on registry + builder of type plugin_measurement_filter::MeasurementFilterPluginBuilder, + with instance { + plugin_measurement_filter::MeasurementFilterPluginBuilder } - cli::CliCommand::ValidateConfig { config } => { - let (_, application) = registry.app_builder.with_config_from_path(config).await?; - info!("Application built"); - - debug!("Only going to validate the configuration"); - application.verify_configurations().await?; - info!("Configuration validated"); - Ok(()) + ); + let registry = tedge_cli::register_plugin!( + if feature "mqtt" is enabled then + register on registry + builder of type plugin_mqtt::MqttPluginBuilder, + with instance { + plugin_mqtt::MqttPluginBuilder::new() } - cli::CliCommand::GetPluginKinds => { - use std::io::Write; - - let mut out = std::io::stdout(); - for name in registry.app_builder.plugin_kind_names() { - writeln!(out, "{}", name).into_diagnostic()?; - } - Ok(()) + ); + let registry = tedge_cli::register_plugin!( + if feature "mqtt" is enabled then + register on registry + builder of type plugin_mqtt_measurement_bridge::MqttMeasurementBridgePluginBuilder, + with instance { + plugin_mqtt_measurement_bridge::MqttMeasurementBridgePluginBuilder::new() } - cli::CliCommand::Doc { plugin_name } => { - let mut registry = registry; - if let Some(plugin_name) = plugin_name { - let printer = registry - .doc_printers - .remove(&plugin_name) - .ok_or_else(|| miette::miette!("Plugin named '{}' not found", plugin_name))?; - - (printer)()?; - } else { - for printer in registry.doc_printers.into_values() { - printer()?; - } - } - - Ok(()) + ); + let registry = tedge_cli::register_plugin!( + if feature "builtin_plugin_notification" is enabled then + register on registry + builder of type plugin_notification::NotificationPluginBuilder, + with instance { + plugin_notification::NotificationPluginBuilder } - } -} - -async fn run( - cancel_sender: TedgeApplicationCancelSender, - application: TedgeApplication, -) -> miette::Result<()> { - info!("Booting app now."); - let mut run_fut = Box::pin(application.run()); - - let kill_app = |fut| -> miette::Result<()> { - error!("Killing application"); - drop(fut); - miette::bail!("Application killed") - }; - - let res = tokio::select! { - res = &mut run_fut => { - res - }, - - _int = tokio::signal::ctrl_c() => { - if !cancel_sender.is_cancelled() { - info!("Shutting down..."); - cancel_sender.cancel_app(); - tokio::select! { - res = &mut run_fut => res, - _ = tokio::signal::ctrl_c() => return kill_app(run_fut), - } - } else { - return kill_app(run_fut); - } - }, - }; + ); - info!("Bye"); - Ok(res?) + tedge_cli::run_app(args, registry).await } diff --git a/tedge/src/registry.rs b/tedge/src/registry.rs new file mode 100644 index 00000000..5db4da45 --- /dev/null +++ b/tedge/src/registry.rs @@ -0,0 +1,78 @@ +use std::collections::HashMap; +use std::collections::HashSet; + +use tedge_core::TedgeApplicationBuilder; + +/// Helper type for registering PluginBuilder instances and doc-printing functions +pub struct Registry { + pub app_builder: TedgeApplicationBuilder, + pub plugin_kinds: HashSet<String>, + pub doc_printers: HashMap<String, Box<dyn FnOnce() -> Result<(), miette::Error>>>, +} + +impl Registry { + pub fn new() -> Self { + Registry { + app_builder: tedge_core::TedgeApplication::builder(), + plugin_kinds: HashSet::new(), + doc_printers: HashMap::new(), + } + } +} + +#[macro_export] +macro_rules! register_plugin { + (if feature $cfg:tt is enabled then + register on $registry:ident + builder of type $pluginbuilder:ty, + with instance $pbinstance:expr + ) => {{ + cfg_if::cfg_if! { + if #[cfg(feature = $cfg)] { + let kind_name: &'static str = <$pluginbuilder as tedge_api::PluginBuilder<tedge_core::PluginDirectory>>::kind_name(); + info!(%kind_name, "Registering plugin builder"); + let mut registry = $registry; + if !registry.plugin_kinds.insert(kind_name.to_string()) { + miette::bail!("Plugin kind '{}' was already registered, cannot register!", kind_name) + } + + let kind_name_str = kind_name.to_string(); + registry.doc_printers.insert(kind_name.to_string(), Box::new(move || { + use std::io::Write; + use miette::IntoDiagnostic; + use pretty::Arena; + + let mut stdout = std::io::stdout(); + if let Some(config_desc) = <$pluginbuilder as tedge_api::PluginBuilder<tedge_core::PluginDirectory>>::kind_configuration() { + let terminal_width = term_size::dimensions().map(|(w, _)| w).unwrap_or(80); + let arena = Arena::new(); + + let rendered_doc = tedge_cli::config::as_terminal_doc(&config_desc, &arena); + + let mut output = String::new(); + rendered_doc.render_fmt(terminal_width, &mut output).into_diagnostic()?; + + writeln!(stdout, " ----- Documentation for plugin '{}'", kind_name_str) + .into_diagnostic()?; + + writeln!(stdout, "{}", output).into_diagnostic()?; + } else { + let msg = format!(" Documentation for plugin '{}' is unavailable", kind_name); + writeln!(stdout, "{}", nu_ansi_term::Color::Red.bold().paint(msg)) + .into_diagnostic()?; + } + Ok(()) + })); + + Registry { + app_builder: registry.app_builder.with_plugin_builder($pbinstance), + plugin_kinds: registry.plugin_kinds, + doc_printers: registry.doc_printers, + } + } else { + tracing::trace!("Not supporting plugins of type {}", std::stringify!($pluginbuilder)); + $registry + } + } + }} +} |