error.rs
1 use std::{fmt, process::ExitCode}; 2 3 use error_stack::{report, Context, Report, Result, ResultExt}; 4 5 /// Sink error. 6 /// 7 /// Sink developers should default to returning `SinkError::Temporary` for all errors. 8 /// `SinkError::Configuration` should be returned for configuration-related errors. 9 /// `SinkError::Fatal` should only be returned from `Sink::handle_data` and `Sink::handle_invalidate`. 10 #[derive(Debug)] 11 pub enum SinkError { 12 /// Configuration error. Should not retry. 13 Configuration, 14 /// Temporary error. Should retry. 15 Temporary, 16 /// Fatal error. Should not retry. 17 Fatal, 18 // Status server error 19 Status, 20 // Persistence error 21 Persistence, 22 // Load script error 23 LoadScript, 24 } 25 26 pub trait ReportExt { 27 fn to_exit_code(&self) -> ExitCode; 28 } 29 30 impl error_stack::Context for SinkError {} 31 32 impl fmt::Display for SinkError { 33 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 34 match self { 35 SinkError::Configuration => f.write_str("sink configuration error"), 36 SinkError::Temporary => f.write_str("temporary sink error"), 37 SinkError::Fatal => f.write_str("fatal sink error"), 38 SinkError::Persistence => f.write_str("persistence client operation failed"), 39 SinkError::Status => f.write_str("status server operation failed"), 40 SinkError::LoadScript => f.write_str("load script failed"), 41 } 42 } 43 } 44 45 impl<T> ReportExt for Result<T, SinkError> { 46 fn to_exit_code(&self) -> ExitCode { 47 match self { 48 Ok(_) => ExitCode::SUCCESS, 49 Err(err) => { 50 eprintln!("{:?}", err); 51 // Exit codes based on sysexits.h 52 match err.downcast_ref::<SinkError>() { 53 Some(SinkError::Configuration) => ExitCode::from(78), 54 Some(SinkError::Temporary) => ExitCode::from(75), 55 Some(SinkError::Fatal) => ExitCode::FAILURE, 56 Some(_) => ExitCode::FAILURE, 57 None => ExitCode::FAILURE, 58 } 59 } 60 } 61 } 62 } 63 64 impl SinkError { 65 pub fn configuration(reason: &str) -> Report<SinkError> { 66 report!(SinkError::Configuration).attach_printable(reason.to_string()) 67 } 68 pub fn temporary(reason: &str) -> Report<SinkError> { 69 report!(SinkError::Temporary).attach_printable(reason.to_string()) 70 } 71 pub fn fatal(reason: &str) -> Report<SinkError> { 72 report!(SinkError::Fatal).attach_printable(reason.to_string()) 73 } 74 pub fn status(reason: &str) -> Report<SinkError> { 75 report!(SinkError::Status).attach_printable(reason.to_string()) 76 } 77 pub fn load_script(reason: &str) -> Report<SinkError> { 78 report!(SinkError::LoadScript).attach_printable(reason.to_string()) 79 } 80 } 81 82 pub trait SinkErrorResultExt { 83 type Ok; 84 fn configuration(self, reason: &str) -> Result<Self::Ok, SinkError>; 85 fn temporary(self, reason: &str) -> Result<Self::Ok, SinkError>; 86 fn fatal(self, reason: &str) -> Result<Self::Ok, SinkError>; 87 fn status(self, reason: &str) -> Result<Self::Ok, SinkError>; 88 fn persistence(self, reason: &str) -> Result<Self::Ok, SinkError>; 89 fn load_script(self, reason: &str) -> Result<Self::Ok, SinkError>; 90 } 91 92 impl<T, C> SinkErrorResultExt for core::result::Result<T, C> 93 where 94 C: Context, 95 { 96 type Ok = T; 97 98 fn configuration(self, reason: &str) -> Result<T, SinkError> { 99 self.change_context(SinkError::Configuration) 100 .attach_printable(reason.to_string()) 101 } 102 103 fn temporary(self, reason: &str) -> Result<T, SinkError> { 104 self.change_context(SinkError::Temporary) 105 .attach_printable(reason.to_string()) 106 } 107 108 fn fatal(self, reason: &str) -> Result<T, SinkError> { 109 self.change_context(SinkError::Fatal) 110 .attach_printable(reason.to_string()) 111 } 112 113 fn status(self, reason: &str) -> Result<T, SinkError> { 114 self.change_context(SinkError::Status) 115 .attach_printable(reason.to_string()) 116 } 117 118 fn persistence(self, reason: &str) -> Result<T, SinkError> { 119 self.change_context(SinkError::Persistence) 120 .attach_printable(reason.to_string()) 121 } 122 123 fn load_script(self, reason: &str) -> Result<T, SinkError> { 124 self.change_context(SinkError::LoadScript) 125 .attach_printable(reason.to_string()) 126 } 127 } 128 129 pub trait SinkErrorReportExt { 130 type Ok; 131 fn configuration(self, reason: &str) -> Report<SinkError>; 132 fn temporary(self, reason: &str) -> Report<SinkError>; 133 fn fatal(self, reason: &str) -> Report<SinkError>; 134 fn status(self, reason: &str) -> Report<SinkError>; 135 fn load_script(self, reason: &str) -> Report<SinkError>; 136 } 137 138 impl<C> SinkErrorReportExt for Report<C> { 139 type Ok = (); 140 141 fn configuration(self, reason: &str) -> Report<SinkError> { 142 self.change_context(SinkError::Configuration) 143 .attach_printable(reason.to_string()) 144 } 145 146 fn temporary(self, reason: &str) -> Report<SinkError> { 147 self.change_context(SinkError::Temporary) 148 .attach_printable(reason.to_string()) 149 } 150 151 fn fatal(self, reason: &str) -> Report<SinkError> { 152 self.change_context(SinkError::Fatal) 153 .attach_printable(reason.to_string()) 154 } 155 156 fn status(self, reason: &str) -> Report<SinkError> { 157 self.change_context(SinkError::Status) 158 .attach_printable(reason.to_string()) 159 } 160 161 fn load_script(self, reason: &str) -> Report<SinkError> { 162 self.change_context(SinkError::LoadScript) 163 .attach_printable(reason.to_string()) 164 } 165 }