error.rs (4382B)
1 use thiserror::Error; 2 3 #[derive(Debug, Error)] 4 pub enum RuntimeConfigError { 5 #[error("failed to load configuration from {path}: {source}")] 6 Load { 7 path: std::path::PathBuf, 8 #[source] 9 source: config::ConfigError, 10 }, 11 } 12 13 #[cfg(feature = "cli")] 14 #[derive(Debug, Error)] 15 pub enum RuntimeCliError { 16 #[error(transparent)] 17 Parse(#[from] clap::Error), 18 19 #[error("configuration path is required; no implicit cwd-rooted default is used")] 20 MissingConfigPath, 21 } 22 23 #[derive(Debug, Error)] 24 pub enum RuntimeTracingError { 25 #[error(transparent)] 26 Log(#[from] radroots_log::Error), 27 28 #[error(transparent)] 29 Paths(#[from] radroots_runtime_paths::RadrootsRuntimePathsError), 30 } 31 32 #[derive(Debug, Error)] 33 pub enum RuntimeProtectedFileError { 34 #[error("failed to create directory {path}: {source}")] 35 CreateDir { 36 path: std::path::PathBuf, 37 #[source] 38 source: std::io::Error, 39 }, 40 #[error("protected secret file io error at {path}: {source}")] 41 Io { 42 path: std::path::PathBuf, 43 #[source] 44 source: std::io::Error, 45 }, 46 #[error("failed to seal protected secret file {path}: {message}")] 47 Seal { 48 path: std::path::PathBuf, 49 message: String, 50 }, 51 #[error("failed to decode protected secret file {path}: {message}")] 52 Decode { 53 path: std::path::PathBuf, 54 message: String, 55 }, 56 #[error("failed to open protected secret file {path}: {message}")] 57 Open { 58 path: std::path::PathBuf, 59 message: String, 60 }, 61 #[error("failed to update secret permissions for {path}: {message}")] 62 Permissions { 63 path: std::path::PathBuf, 64 message: String, 65 }, 66 } 67 68 #[derive(Debug, Error)] 69 pub enum RuntimeError { 70 #[error(transparent)] 71 Config(#[from] Box<RuntimeConfigError>), 72 73 #[cfg(feature = "cli")] 74 #[error(transparent)] 75 Cli(#[from] RuntimeCliError), 76 77 #[error(transparent)] 78 Tracing(#[from] RuntimeTracingError), 79 } 80 81 impl From<RuntimeConfigError> for RuntimeError { 82 fn from(value: RuntimeConfigError) -> Self { 83 Self::Config(Box::new(value)) 84 } 85 } 86 87 #[cfg(test)] 88 mod tests { 89 use super::{RuntimeConfigError, RuntimeError, RuntimeProtectedFileError, RuntimeTracingError}; 90 use std::error::Error as _; 91 use std::path::PathBuf; 92 93 #[test] 94 fn runtime_config_error_message_and_source_are_accessible() { 95 let err = RuntimeConfigError::Load { 96 path: PathBuf::from("config.toml"), 97 source: config::ConfigError::Message("invalid config".to_string()), 98 }; 99 let display = err.to_string(); 100 assert!(display.contains("config.toml")); 101 assert!(display.contains("invalid config")); 102 assert!(err.source().is_some()); 103 } 104 105 #[test] 106 fn runtime_error_conversions_include_config_and_tracing_variants() { 107 let cfg = RuntimeConfigError::Load { 108 path: PathBuf::from("runtime.toml"), 109 source: config::ConfigError::Message("bad".to_string()), 110 }; 111 let runtime_from_cfg: RuntimeError = cfg.into(); 112 assert!(runtime_from_cfg.to_string().contains("runtime.toml")); 113 assert!(runtime_from_cfg.source().is_some()); 114 115 let tracing = 116 RuntimeTracingError::from(radroots_log::Error::Msg("log-failure".to_string())); 117 let runtime_from_tracing: RuntimeError = tracing.into(); 118 assert!(runtime_from_tracing.to_string().contains("log-failure")); 119 assert!(runtime_from_tracing.source().is_none()); 120 121 let paths = RuntimeTracingError::from( 122 radroots_runtime_paths::RadrootsRuntimePathsError::MissingHomeDir { 123 platform: radroots_runtime_paths::RadrootsPlatform::Linux, 124 }, 125 ); 126 let runtime_from_paths: RuntimeError = paths.into(); 127 assert!(runtime_from_paths.to_string().contains("home directory")); 128 assert!(runtime_from_paths.source().is_none()); 129 } 130 131 #[test] 132 fn protected_file_error_displays_path_and_message() { 133 let err = RuntimeProtectedFileError::Open { 134 path: PathBuf::from("identity.secret.json"), 135 message: "missing wrapping key".to_string(), 136 }; 137 let display = err.to_string(); 138 assert!(display.contains("identity.secret.json")); 139 assert!(display.contains("missing wrapping key")); 140 } 141 }