/ runners / runner-common / src / error.rs
error.rs
  1  use error_stack::{report, Context, Report, ResultExt};
  2  use std::fmt;
  3  
  4  #[derive(Debug)]
  5  pub enum RunnerError {
  6      Internal(String),
  7      MissingArgument(String),
  8      InvalidArgument(String, String),
  9      NotFound(String),
 10      AlreadyExists(String),
 11  }
 12  impl RunnerError {
 13      pub fn internal(message: &str) -> Report<RunnerError> {
 14          report!(RunnerError::Internal(message.to_string())).attach_printable(message.to_string())
 15      }
 16      pub fn missing_argument(argument: &str) -> Report<RunnerError> {
 17          report!(RunnerError::MissingArgument(argument.to_string()))
 18              .attach_printable(format!("missing required argument: {argument}"))
 19      }
 20      pub fn invalid_argument(argument: &str, reason: &str) -> Report<RunnerError> {
 21          report!(RunnerError::InvalidArgument(
 22              argument.to_string(),
 23              reason.to_string()
 24          ))
 25          .attach_printable(format!("invalid argument {argument}: {reason}"))
 26      }
 27      pub fn not_found(name: &str) -> Report<RunnerError> {
 28          report!(RunnerError::NotFound(name.to_string()))
 29              .attach_printable(format!("{} not found", name))
 30      }
 31      pub fn already_exists(name: &str) -> Report<RunnerError> {
 32          report!(RunnerError::AlreadyExists(name.to_string()))
 33              .attach_printable(format!("{} already exists", name))
 34      }
 35  
 36      pub fn to_tonic_status(&self) -> tonic::Status {
 37          match self {
 38              // TODO: include the internal error message in the tonic message but only for local runner
 39              RunnerError::Internal(_message) => tonic::Status::internal("internal error"),
 40              RunnerError::MissingArgument(argument) => {
 41                  tonic::Status::invalid_argument(format!("missing required argument: {argument}"))
 42              }
 43              RunnerError::InvalidArgument(argument, reason) => {
 44                  tonic::Status::invalid_argument(format!("invalid argument {argument}: {reason}"))
 45              }
 46              RunnerError::NotFound(name) => tonic::Status::not_found(format!("{} not found", name)),
 47              RunnerError::AlreadyExists(name) => {
 48                  tonic::Status::already_exists(format!("{} already exists", name))
 49              }
 50          }
 51      }
 52  }
 53  impl Context for RunnerError {}
 54  impl fmt::Display for RunnerError {
 55      fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 56          f.write_str("local runner failed")
 57      }
 58  }
 59  
 60  pub type RunnerResult<T> = error_stack::Result<T, RunnerError>;
 61  
 62  pub trait RunnerResultExt {
 63      type Ok;
 64      fn internal(self, message: &str) -> RunnerResult<Self::Ok>;
 65      fn missing_argument(self, argument: &str) -> RunnerResult<Self::Ok>;
 66      fn invalid_argument(self, argument: &str, reason: &str) -> RunnerResult<Self::Ok>;
 67      fn not_found(self, name: &str) -> RunnerResult<Self::Ok>;
 68      fn already_exists(self, name: &str) -> RunnerResult<Self::Ok>;
 69  }
 70  
 71  pub trait RunnerReportExt {
 72      fn internal(self, message: &str) -> Report<RunnerError>;
 73  }
 74  
 75  impl<T, C> RunnerResultExt for core::result::Result<T, C>
 76  where
 77      C: Context,
 78  {
 79      type Ok = T;
 80  
 81      fn internal(self, message: &str) -> RunnerResult<T> {
 82          self.change_context(RunnerError::Internal(message.to_string()))
 83              .attach_printable(message.to_string())
 84      }
 85  
 86      fn missing_argument(self, argument: &str) -> RunnerResult<T> {
 87          self.change_context(RunnerError::MissingArgument(argument.to_string()))
 88              .attach_printable(format!("missing required argument {argument}"))
 89      }
 90  
 91      fn invalid_argument(self, argument: &str, reason: &str) -> RunnerResult<T> {
 92          self.change_context(RunnerError::InvalidArgument(
 93              argument.to_string(),
 94              reason.to_string(),
 95          ))
 96          .attach_printable(format!("invalid argument {argument}: {reason}"))
 97      }
 98  
 99      fn not_found(self, name: &str) -> RunnerResult<T> {
100          self.change_context(RunnerError::NotFound(name.to_string()))
101              .attach_printable(format!("{name} not found"))
102      }
103  
104      fn already_exists(self, name: &str) -> RunnerResult<T> {
105          self.change_context(RunnerError::AlreadyExists(name.to_string()))
106              .attach_printable(format!("{name} already exists"))
107      }
108  }
109  
110  impl<C> RunnerReportExt for Report<C> {
111      fn internal(self, message: &str) -> Report<RunnerError> {
112          self.change_context(RunnerError::Internal(message.to_string()))
113              .attach_printable(message.to_string())
114      }
115  }