diff options
author | Marcel Müller <m.mueller@ifm.com> | 2022-05-24 11:00:51 +0200 |
---|---|---|
committer | Marcel Müller <m.mueller@ifm.com> | 2022-05-24 11:53:17 +0200 |
commit | bed3ed4cf7df1be1f015dc8f9a33b2997e584f59 (patch) | |
tree | 482b9f1f38341f36f8b71aafe4af8b7f3eb10347 | |
parent | a6cce594489cff20bc5540127dc4fa074d7576e7 (diff) |
Convert TedgeCore to a more complex error type
Errors until now where a flat enum. This did not reflect the multitude
of ways the application can produce errors. This patch tries to align
this reality with the error reporting.
Signed-off-by: Marcel Müller <m.mueller@ifm.com>
-rw-r--r-- | Cargo.lock | 5 | ||||
-rw-r--r-- | crates/core/tedge_core/Cargo.toml | 1 | ||||
-rw-r--r-- | crates/core/tedge_core/src/communication.rs | 22 | ||||
-rw-r--r-- | crates/core/tedge_core/src/configuration.rs | 28 | ||||
-rw-r--r-- | crates/core/tedge_core/src/errors.rs | 255 | ||||
-rw-r--r-- | crates/core/tedge_core/src/lib.rs | 135 | ||||
-rw-r--r-- | crates/core/tedge_core/src/plugin_task.rs | 200 | ||||
-rw-r--r-- | crates/core/tedge_core/src/reactor.rs | 109 | ||||
-rw-r--r-- | crates/core/tedge_core/tests/check_concurrent_messages.rs | 4 | ||||
-rw-r--r-- | crates/core/tedge_core/tests/plugin_does_not_shutdown.rs | 2 | ||||
-rw-r--r-- | crates/core/tedge_core/tests/plugin_does_not_support_message.rs | 6 | ||||
-rw-r--r-- | crates/core/tedge_core/tests/plugin_panic_lifecycle.rs | 2 | ||||
-rw-r--r-- | crates/core/tedge_core/tests/plugin_panics_in_msg_handler.rs | 2 | ||||
-rw-r--r-- | crates/core/tedge_core/tests/plugin_send_to_self_name.rs | 2 | ||||
-rw-r--r-- | crates/core/tedge_core/tests/verify_configuration_fails.rs | 12 |
15 files changed, 459 insertions, 326 deletions
@@ -1423,9 +1423,9 @@ checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb" [[package]] name = "itertools" -version = "0.10.1" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" dependencies = [ "either", ] @@ -3460,6 +3460,7 @@ dependencies = [ "criterion", "env_logger 0.9.0", "futures", + "itertools", "miette", "serde", "static_assertions", diff --git a/crates/core/tedge_core/Cargo.toml b/crates/core/tedge_core/Cargo.toml index 7a579b1b..0a3e8103 100644 --- a/crates/core/tedge_core/Cargo.toml +++ b/crates/core/tedge_core/Cargo.toml @@ -17,6 +17,7 @@ toml = "0.5.8" tracing = "0.1" tedge_api = { path = "../tedge_api" } +itertools = "0.10.3" [features] tracing-off = ["tracing/release_max_level_off"] diff --git a/crates/core/tedge_core/src/communication.rs b/crates/core/tedge_core/src/communication.rs index 7c29e52e..80039949 100644 --- a/crates/core/tedge_core/src/communication.rs +++ b/crates/core/tedge_core/src/communication.rs @@ -1,13 +1,14 @@ use std::collections::HashMap; use std::sync::Arc; +use itertools::Itertools; use tedge_api::address::MessageSender; use tedge_api::error::DirectoryError; use tedge_api::message::MessageType; use tedge_api::plugin::PluginDirectory as ApiPluginDirectory; use tedge_api::Address; -use crate::errors::TedgeApplicationError; +use crate::errors::{PluginInstantiationError, TedgeApplicationError}; /// Type for taking care of addresses /// @@ -22,10 +23,16 @@ pub struct CorePluginDirectory { impl CorePluginDirectory { pub(crate) fn collect_from<I>(iter: I) -> Result<Self, TedgeApplicationError> where - I: std::iter::IntoIterator<Item = Result<(String, PluginInfo), TedgeApplicationError>>, + I: std::iter::IntoIterator<Item = Result<(String, PluginInfo), PluginInstantiationError>>, { + let (oks, errors): (_, Vec<_>) = iter.into_iter().partition_result(); + + if !errors.is_empty() { + return Err(TedgeApplicationError::PluginInstantiationsError { errors }); + } + Ok(CorePluginDirectory { - plugins: iter.into_iter().collect::<Result<HashMap<_, _>, _>>()?, + plugins: oks, core_communicator: MessageSender::new(Default::default()), }) } @@ -287,9 +294,8 @@ mod tests { ); let channel_size = 1; - let tedge_builder = crate::TedgeApplication::builder() - .with_plugin_builder(testplugin::Builder {}) - .unwrap(); + let tedge_builder = + crate::TedgeApplication::builder().with_plugin_builder(testplugin::Builder {}); let config: TedgeConfiguration = toml::de::from_str(&conf).unwrap(); let directory_iter = config.plugins().iter().map(|(pname, pconfig)| { @@ -303,9 +309,7 @@ mod tests { .cloned() .collect::<Vec<MessageType>>() }) - .ok_or_else(|| { - TedgeApplicationError::UnknownPluginKind(pconfig.kind().as_ref().to_string()) - })?; + .ok_or_else(|| panic!())?; Ok(( pname.to_string(), diff --git a/crates/core/tedge_core/src/configuration.rs b/crates/core/tedge_core/src/configuration.rs index 1403215a..7e7fc868 100644 --- a/crates/core/tedge_core/src/configuration.rs +++ b/crates/core/tedge_core/src/configuration.rs @@ -4,11 +4,11 @@ use std::{ path::{Path, PathBuf}, }; +use miette::IntoDiagnostic; use tedge_api::PluginBuilder; use tracing::debug; -use crate::communication::PluginDirectory; -use crate::errors::TedgeApplicationError; +use crate::{communication::PluginDirectory, errors::PluginConfigurationError}; #[derive(serde::Deserialize, Debug)] pub struct TedgeConfiguration { @@ -76,39 +76,37 @@ impl InstanceConfiguration { &self, builder: &Box<dyn PluginBuilder<PluginDirectory>>, root_config_path: &Path, - ) -> crate::errors::Result<toml::Value> { + ) -> Result<toml::Value, miette::Error> { match self { InstanceConfiguration::Config(cfg) => builder .verify_configuration(&cfg) .await - .map_err(TedgeApplicationError::PluginConfigVerificationFailed) .map(|_| cfg.to_owned()), InstanceConfiguration::ConfigFilePath(path) => { async fn inner( builder: &Box<dyn PluginBuilder<PluginDirectory>>, root_config_path: &Path, path: &Path, - ) -> crate::errors::Result<toml::Value> { + ) -> Result<toml::Value, miette::Error> { let file_path = root_config_path .parent() - .ok_or_else(|| { - TedgeApplicationError::PathNotAFilePath(root_config_path.to_path_buf()) + .ok_or_else(|| PluginConfigurationError::PathNotAFilePath { + path: root_config_path.to_path_buf(), })? .join(path); debug!("Reading config file: {}", file_path.display()); let file_contents = - tokio::fs::read_to_string(file_path).await.map_err(|_| { - TedgeApplicationError::PluginConfigReadFailed(path.to_path_buf()) + tokio::fs::read_to_string(file_path).await.map_err(|e| { + PluginConfigurationError::PathNotReadable { + path: path.to_path_buf(), + error: e, + } })?; - let cfg = toml::from_str(&file_contents)?; + let cfg = toml::from_str(&file_contents).into_diagnostic()?; - builder - .verify_configuration(&cfg) - .await - .map_err(TedgeApplicationError::PluginConfigVerificationFailed) - .map(|_| cfg) + builder.verify_configuration(&cfg).await.map(|_| cfg) } inner(builder, root_config_path, &path).await diff --git a/crates/core/tedge_core/src/errors.rs b/crates/core/tedge_core/src/errors.rs index 17cdef1d..0fd8c547 100644 --- a/crates/core/tedge_core/src/errors.rs +++ b/crates/core/tedge_core/src/errors.rs @@ -1,66 +1,239 @@ use std::path::PathBuf; +use std::time::Duration; #[derive(Debug, miette::Diagnostic, thiserror::Error)] pub enum TedgeApplicationError { - #[error("Plugin instantiation failed")] - PluginInstantiationFailed(tedge_api::error::PluginError), + #[error("Could not complete building the tedge application because of the following errors")] + ApplicationBuilderErrors { + #[related] + errors: Vec<TedgeApplicationBuilderError>, + }, + #[error("Could not verify the configuration of one or more plugins")] + PluginConfigVerificationsError { + #[related] + errors: Vec<PluginConfigurationError>, + }, + #[error("Could not instantiate one or more plugins")] + PluginInstantiationsError { + #[related] + errors: Vec<PluginInstantiationError>, + }, + #[error("Could not shutdown one or more plugins")] + PluginLifecycleErrors { + #[related] + errors: Vec<PluginLifecycleError>, + }, +} + +#[derive(Debug, miette::Diagnostic, thiserror::Error)] +pub enum TedgeApplicationBuilderError { + #[error("A plugin builder with the duplicate name '{name}' was registered")] + #[diagnostic(help("The duplicate name was registered by the builder name '{builder_name}'"))] + DuplicateKind { + name: String, + builder_name: &'static str, + }, + #[error("Could not read configuration at {:?}", path)] + PathNotReadable { + path: PathBuf, + #[source] + error: std::io::Error, + }, + #[error("Could not parse configuration at {:?}", path)] + ConfigNotParseable { + path: PathBuf, + #[source] + error: toml::de::Error, + }, +} + +#[derive(Debug, miette::Diagnostic, thiserror::Error)] +pub enum PluginConfigurationError { + #[error(transparent)] + #[diagnostic(transparent)] + Verification(PluginConfigVerificationError), + #[error("Given path is not a filepath: {:?}", path)] + PathNotAFilePath { path: PathBuf }, + #[error("Could not read from path: {:?}", path)] + PathNotReadable { + path: PathBuf, + #[source] + error: std::io::Error, + }, + #[error(transparent)] + #[diagnostic(transparent)] + UnknownKind(PluginKindUnknownError), +} - #[error("Plugin errored during startup")] - PluginErroredOnStart(tedge_api::error::PluginError), +#[derive(miette::Diagnostic, Debug, thiserror::Error)] +#[error("No plugin with kind '{name}' is currently registered with the application")] +pub struct PluginKindUnknownError { + pub name: String, + #[diagnostic(help)] + pub alternatives: Option<String>, +} - #[error("Failed to read configuration file")] - ConfigReadFailed(std::io::Error), +#[derive(Debug, thiserror::Error)] +#[error("Failed to instantiate plugin '{name}'")] +pub struct PluginConfigVerificationError { + pub name: String, + pub error: tedge_api::error::PluginError, +} - #[error("Path is not a file path {0}")] - PathNotAFilePath(PathBuf), +impl miette::Diagnostic for PluginConfigVerificationError { + fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn miette::Diagnostic> + 'a>> { + Some(Box::new(std::iter::once(self.error.as_ref()))) + } +} - #[error("Config verification failed")] - PluginConfigVerificationFailed(tedge_api::error::PluginError), +#[derive(miette::Diagnostic, Debug, thiserror::Error)] +#[error("No configuration for plugin '{name}' was found.")] +#[diagnostic(help("Add a configuration block for plugin '{name}'"))] +pub struct PluginConfigurationNotFoundError { + pub name: String, +} - #[error("Failed to deserialize configuration")] - PluginConfigurationDeserializationFailed(#[from] toml::de::Error), +#[derive(miette::Diagnostic, Debug, thiserror::Error)] +pub enum PluginInstantiationError { + #[error(transparent)] + #[diagnostic(transparent)] + BuilderInstantiation(PluginBuilderInstantiationError), + #[error(transparent)] + #[diagnostic(transparent)] + KindNotFound(PluginKindUnknownError), + #[error(transparent)] + #[diagnostic(transparent)] + ConfigurationNotFound(PluginConfigurationNotFoundError), + #[error(transparent)] + #[diagnostic(transparent)] + ConfigurationVerificationFailed(PluginConfigVerificationError), +} + +#[derive(Debug, thiserror::Error)] +#[error("Failed to instantiate plugin '{name}'")] +pub struct PluginBuilderInstantiationError { + pub name: String, + pub error: tedge_api::error::PluginError, +} - #[error("Failed to read configuration file: {0}")] - PluginConfigReadFailed(PathBuf), +impl miette::Diagnostic for PluginBuilderInstantiationError { + fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn miette::Diagnostic> + 'a>> { + Some(Box::new(std::iter::once(self.error.as_ref()))) + } +} - #[error("Plugin kind exists already: {0}")] - PluginKindExists(String), +#[derive(miette::Diagnostic, Debug, thiserror::Error)] +pub enum PluginLifecycleError { + #[error(transparent)] + #[diagnostic(transparent)] + PluginStartPanicked(PluginStartPanicked), + #[error(transparent)] + #[diagnostic(transparent)] + PluginStartFailed(PluginStartFailed), + #[error(transparent)] + #[diagnostic(transparent)] + PluginMainPanicked(PluginMainPanicked), + #[error(transparent)] + #[diagnostic(transparent)] + PluginMainFailed(PluginMainFailed), + #[error(transparent)] + #[diagnostic(transparent)] + PluginMessageHandlerPanicked(PluginMessageHandlerPanicked), + #[error(transparent)] + #[diagnostic(transparent)] + PluginMessageHandlerFailed(PluginMessageHandlerFailed), + #[error(transparent)] + #[diagnostic(transparent)] + PluginStopPanicked(PluginStopPanicked), + #[error(transparent)] + #[diagnostic(transparent)] + PluginStopTimeout(PluginStopTimeout), + #[error(transparent)] + #[diagnostic(transparent)] + PluginStopFailed(PluginStopFailed), +} - #[error("The following Plugin kind are not covered in the configuration: {0}")] - UnconfiguredPlugins(crate::utils::CommaSeperatedString), +#[derive(miette::Diagnostic, Debug, thiserror::Error)] +#[error("Plugin '{name}' panicked while starting up")] +pub struct PluginStartPanicked { + pub name: String, +} - #[error("The following Plugin has no configuration: {0}")] - PluginConfigMissing(String), +#[derive(Debug, thiserror::Error)] +#[error("Plugin '{name}' encountered an error while starting up")] +pub struct PluginStartFailed { + pub name: String, + pub error: tedge_api::PluginError, +} - #[error("Unknown Plugin kind: {0}")] - UnknownPluginKind(String), +impl miette::Diagnostic for PluginStartFailed { + fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn miette::Diagnostic> + 'a>> { + Some(Box::new(std::iter::once(self.error.as_ref()))) + } +} - #[error("Plugin '{0}' shutdown timeouted")] - PluginShutdownTimeout(String), +#[derive(miette::Diagnostic, Debug, thiserror::Error)] +#[error("Plugin '{name}' panicked while running main")] +pub struct PluginMainPanicked { + pub name: String, +} - #[error("Plugin '{0}' shutdown errored")] - PluginShutdownError(String), +#[derive(Debug, thiserror::Error)] +#[error("Plugin '{name}' encountered an error while running main")] +pub struct PluginMainFailed { + pub name: String, + pub error: tedge_api::PluginError, +} - #[error("Plugin '{0}' setup paniced")] - PluginSetupPaniced(String), +impl miette::Diagnostic for PluginMainFailed { + fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn miette::Diagnostic> + 'a>> { + Some(Box::new(std::iter::once(self.error.as_ref()))) + } +} - #[error("Plugin '{0}' setup failed")] - PluginSetupFailed(String, tedge_api::error::PluginError), +#[derive(miette::Diagnostic, Debug, thiserror::Error)] +#[error("Plugin '{name}' panicked while handling messages of type '{handled_message_type}'")] +pub struct PluginMessageHandlerPanicked { + pub name: String, + pub handled_message_type: String, +} - #[error("Plugin '{0}' setup paniced")] - PluginMainPaniced(String), +#[derive(Debug, thiserror::Error)] +#[error("Plugin '{name}' panicked while starting up")] +pub struct PluginMessageHandlerFailed { + pub name: String, + pub handled_message_type: String, + pub error: tedge_api::PluginError, +} - #[error("Plugin '{0}' setup failed")] - PluginMainFailed(String, tedge_api::error::PluginError), +impl miette::Diagnostic for PluginMessageHandlerFailed { + fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn miette::Diagnostic> + 'a>> { + Some(Box::new(std::iter::once(self.error.as_ref()))) + } +} - #[error("Plugin '{0}' paniced in message handler")] - PluginMessageHandlerPaniced(String), +#[derive(miette::Diagnostic, Debug, thiserror::Error)] +#[error("Plugin '{name}' panicked while stopping")] +pub struct PluginStopPanicked { + pub name: String, +} - #[error("Plugin message handling for plugin '{0}' failed")] - PluginMessageHandlingFailed(String), +#[derive(miette::Diagnostic, Debug, thiserror::Error)] +#[error("Plugin '{name}' did not stop after a timeout of '{}'")] +pub struct PluginStopTimeout { + pub name: String, + pub timeout_duration: Duration, +} - #[error("Message handling scheduling failed for plugin '{0}'")] - MessageHandlingJobFailed(String, tokio::task::JoinError), +#[derive(Debug, thiserror::Error)] +#[error("Plugin '{name}' encountered an error while stopping")] +pub struct PluginStopFailed { + pub name: String, + pub error: tedge_api::PluginError, } -pub(crate) type Result<T> = std::result::Result<T, TedgeApplicationError>; +impl miette::Diagnostic for PluginStopFailed { + fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn miette::Diagnostic> + 'a>> { + Some(Box::new(std::iter::once(self.error.as_ref()))) + } +} diff --git a/crates/core/tedge_core/src/lib.rs b/crates/core/tedge_core/src/lib.rs index 5fee11ed..8ea4a0fa 100644 --- a/crates/core/tedge_core/src/lib.rs +++ b/crates/core/tedge_core/src/lib.rs @@ -4,29 +4,32 @@ use std::collections::HashMap; use std::path::Path; use std::path::PathBuf; -use miette::IntoDiagnostic; +use itertools::Itertools; use tedge_api::plugin::HandleTypes; use tedge_api::PluginBuilder; use tokio_util::sync::CancellationToken; use tracing::debug; -use tracing::debug_span; + use tracing::event; -use tracing::Instrument; + use tracing::Level; mod communication; pub mod configuration; mod core_task; pub mod errors; +mod message_handler; mod plugin_task; mod reactor; mod utils; -mod message_handler; pub use crate::communication::PluginDirectory; use crate::configuration::PluginInstanceConfiguration; use crate::configuration::TedgeConfiguration; -use crate::errors::Result; +use crate::errors::PluginConfigVerificationError; +use crate::errors::PluginConfigurationError; +use crate::errors::PluginKindUnknownError; +use crate::errors::TedgeApplicationBuilderError; use crate::errors::TedgeApplicationError; /// A TedgeApplication @@ -59,6 +62,7 @@ impl TedgeApplication { TedgeApplicationBuilder { cancellation_token: CancellationToken::new(), plugin_builders: HashMap::new(), + errors: vec![], } } @@ -83,52 +87,67 @@ impl TedgeApplication { /// Run the TedgeApplication that has been setup for running /// /// This function runs as long as there is no shutdown signalled to the application. - #[tracing::instrument(skip(self), level = "debug")] - pub async fn run(self) -> Result<()> { - // This `Reactor` type is only used to seperate the public-interface implementing parts of - // this crate from the orchestration and lifecycle management code bits. + /// + /// # Note + /// + /// This function makes sure that the configuration is verified before the plugins are started. + /// So there is no need to call [TedgeApplication::verify_configuration] before this. + pub async fn run(self) -> Result<(), TedgeApplicationError> { crate::reactor::Reactor(self).run().await } /// Check whether all configured plugin kinds exist (are available in registered plugins) /// and that the configurations for the individual plugins are actually valid. - pub async fn verify_configurations(&self) -> Vec<(String, Result<()>)> { + #[tracing::instrument(skip(self))] + pub async fn verify_configurations(&self) -> Result<(), TedgeApplicationError> { use futures::stream::StreamExt; debug!("Verifying configurations"); - self.config() + let results = self + .config() .plugins() .iter() .map( - |(plugin_name, plugin_cfg): (&String, &PluginInstanceConfiguration)| { - async { - if let Some((_, builder)) = - self.plugin_builders().get(plugin_cfg.kind().as_ref()) - { - debug!("Verifying {}", plugin_cfg.kind().as_ref()); - let res = plugin_cfg - .configuration() - .verify_with_builder(builder, self.config_path()) - .await - .into_diagnostic() - .map_err(TedgeApplicationError::PluginConfigVerificationFailed) - .map(|_| ()); - (plugin_name.to_string(), res) - } else { - ( - plugin_name.to_string(), - Err(TedgeApplicationError::UnknownPluginKind( - plugin_cfg.kind().as_ref().to_string(), - )), - ) - } + |(plugin_name, plugin_cfg): (&String, &PluginInstanceConfiguration)| async { + if let Some((_, builder)) = + self.plugin_builders().get(plugin_cfg.kind().as_ref()) + { + debug!("Verifying {}", plugin_cfg.kind().as_ref()); + let res = plugin_cfg + .configuration() + .verify_with_builder(builder, self.config_path()) + .await + .map_err(|e| { + PluginConfigurationError::Verification( + PluginConfigVerificationError { + name: plugin_name.to_string(), + error: e, + }, + ) + }); + + res + } else { + Err(PluginConfigurationError::UnknownKind( + PluginKindUnknownError { + name: plugin_cfg.kind().as_ref().to_string(), + alternatives: None, + }, + )) } - .instrument(debug_span!("verify configuration", plugin.name = %plugin_name)) }, ) .collect::<futures::stream::FuturesUnordered<_>>() - .collect::<Vec<(String, Result<()>)>>() - .await + .collect::<Vec<Result<_, _>>>() + .await; + + let (_oks, errors): (Vec<_>, Vec<_>) = results.into_iter().partition_result(); + + if !errors.is_empty() { + return Err(TedgeApplicationError::PluginConfigVerificationsError { errors }); + } + + Ok(()) } } @@ -136,6 +155,7 @@ impl TedgeApplication { pub struct TedgeApplicationBuilder { cancellation_token: CancellationToken, plugin_builders: HashMap<String, (HandleTypes, Box<dyn PluginBuilder<PluginDirectory>>)>, + errors: Vec<TedgeApplicationBuilderError>, } impl TedgeApplicationBuilder { @@ -148,10 +168,7 @@ impl TedgeApplicationBuilder { /// running once the application starts up, but merely that the application _knows_ about this /// plugin builder and is able to construct a plugin with this builder, if necessary (e.g. if /// configured in a configuration file). - pub fn with_plugin_builder<PB: PluginBuilder<PluginDirectory>>( - mut self, - builder: PB, - ) -> Result<Self> { + pub fn with_plugin_builder<PB: PluginBuilder<PluginDirectory>>(mut self, builder: PB) -> Self { let handle_types = PB::kind_message_types(); let kind_name = PB::kind_name(); event!( @@ -162,14 +179,17 @@ impl TedgeApplicationBuilder { ); if self.plugin_builders.contains_key(kind_name) { - return Err(TedgeApplicationError::PluginKindExists( - kind_name.to_string(), - )); + self.errors + .push(TedgeApplicationBuilderError::DuplicateKind { + name: kind_name.to_string(), + builder_name: std::any::type_name::<PB>(), + }); + return self; } self.plugin_builders .insert(kind_name.to_string(), (handle_types, Box::new(builder))); - Ok(self) + self } /// Finalize the [`TedgeApplication`] by instantiating it with a `TedgeConfiguration`] @@ -178,14 +198,31 @@ impl TedgeApplicationBuilder { pub async fn with_config_from_path( self, config_path: impl AsRef<Path>, - ) -> Result<(TedgeApplicationCancelSender, TedgeApplication)> { + ) -> Result<(TedgeApplicationCancelSender, TedgeApplication), TedgeApplicationError> { + if !self.errors.is_empty() { + return Err(TedgeApplicationError::ApplicationBuilderErrors { + errors: self.errors, + }); + } let config_path = config_path.as_ref(); debug!(?config_path, "Loading config from path"); - let config_str = tokio::fs::read_to_string(&config_path) - .await - .map_err(TedgeApplicationError::ConfigReadFailed)?; - let config = toml::de::from_str(&config_str)?; + let config_str = tokio::fs::read_to_string(&config_path).await.map_err(|e| { + TedgeApplicationError::ApplicationBuilderErrors { + errors: vec![TedgeApplicationBuilderError::PathNotReadable { + path: config_path.to_path_buf(), + error: e, + }], + } + })?; + let config = toml::de::from_str(&config_str).map_err(|e| { + TedgeApplicationError::ApplicationBuilderErrors { + errors: vec![TedgeApplicationBuilderError::ConfigNotParseable { + path: config_path.to_path_buf(), + error: e, + }], + } + })?; let cancellation = TedgeApplicationCancelSender(self.cancellation_token.clone()); let app = TedgeApplication { config_path: config_path.to_path_buf(), diff --git a/crates/core/tedge_core/src/plugin_task.rs b/crates/core/tedge_core/src/plugin_task.rs index 7f4b994d..031117b3 100644 --- a/crates/core/tedge_core/src/plugin_task.rs +++ b/crates/core/tedge_core/src/plugin_task.rs @@ -9,14 +9,19 @@ use tokio::sync::mpsc::Sender; use tokio::sync::RwLock; use tokio::sync::Semaphore; use tokio_util::sync::CancellationToken; -use tracing::debug; use tracing::error; use tracing::info; use tracing::trace; use tracing::Instrument; -use crate::errors::Result; -use crate::errors::TedgeApplicationError; +use crate::errors::PluginLifecycleError; +use crate::errors::PluginMainFailed; +use crate::errors::PluginMainPanicked; +use crate::errors::PluginStartFailed; +use crate::errors::PluginStartPanicked; +use crate::errors::PluginStopFailed; +use crate::errors::PluginStopPanicked; +use crate::errors::PluginStopTimeout; use crate::message_handler::make_message_handler; /// Type for handling the lifecycle of one individual Plugin instance @@ -66,11 +71,11 @@ impl PluginTask { } impl PluginTask { - pub async fn run_start(&mut self) -> crate::errors::Result<()> { + pub async fn run_start(&mut self) -> Result<(), PluginLifecycleError> { let plugin_name: &str = &self.plugin_name; let mut plug_write = self.plugin.write().await; - // we can use AssertUnwindSafe here because we're _not_ using the plugin after a panic has + // weIch bin ja noch can use AssertUnwindSafe here because we're _not_ using the plugin after a panic has // happened. match std::panic::AssertUnwindSafe(plug_write.plugin_mut().start()) .catch_unwind() @@ -91,13 +96,18 @@ impl PluginTask { // don't make use of the plugin for unwind safety reasons, and the plugin // will be dropped - return Err(TedgeApplicationError::PluginSetupPaniced( - plugin_name.to_string(), + return Err(PluginLifecycleError::PluginStartPanicked( + PluginStartPanicked { + name: plugin_name.to_string(), + }, )); } Ok(res) => { res.map_err(|e| { - TedgeApplicationError::PluginSetupFailed(plugin_name.to_string(), e) + PluginLifecycleError::PluginStartFailed(PluginStartFailed { + name: plugin_name.to_string(), + error: e, + }) })?; } }; @@ -105,7 +115,7 @@ impl PluginTask { Ok(()) } - pub async fn enable_communications(&self) -> crate::errors::Result<()> { + pub async fn enable_communications(&self) -> Result<(), PluginLifecycleError> { |