1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#[derive(thiserror::Error, Debug)]
pub enum Error {
  #[error("Failed to parse user config: {0}")]
  WorkflowConfigError(String),

  #[error("Error while running workflow: {0}")]
  InternalRuntimeError(String),

  #[error("Failed with exit code: {0:?}")]
  Failed(usize),

  #[error("IO error: {0}")]
  IOError(#[from] std::io::Error),

  #[error("Failed to initialize workflow: {0}")]
  InitError(String),

  #[error("Unsupported feature: {0}")]
  UnsupportedFeature(String),
}

impl Error {
  pub fn workflow_config_error<T: ToString>(message: T) -> Self {
    Self::WorkflowConfigError(message.to_string())
  }

  pub fn internal_runtime_error<T: ToString>(message: T) -> Self {
    Self::InternalRuntimeError(message.to_string())
  }

  pub fn io_error(source: std::io::Error) -> Self {
    Self::IOError(source)
  }

  pub fn failed(exit_code: usize) -> Self {
    Self::Failed(exit_code)
  }

  pub fn unsupported_feature<T: ToString>(message: T) -> Self {
    Self::UnsupportedFeature(message.to_string())
  }

  pub fn init_error<T: ToString>(message: T) -> Self {
    Self::InitError(message.to_string())
  }
}

// implement Eq and PartialEq for Error so that we can compare errors in tests
impl PartialEq for Error {
  fn eq(&self, other: &Self) -> bool {
    match (self, other) {
      (Self::WorkflowConfigError(a), Self::WorkflowConfigError(b)) => a == b,
      (Self::InternalRuntimeError(a), Self::InternalRuntimeError(b)) => a == b,
      (Self::Failed(a), Self::Failed(b)) => a == b,
      (Self::IOError(a), Self::IOError(b)) => a.kind() == b.kind(),
      (Self::UnsupportedFeature(a), Self::UnsupportedFeature(b)) => a == b,
      _ => false,
    }
  }
}

#[cfg(test)]
mod tests {
  use super::*;

  #[test]
  fn test_eq() {
    assert_eq!(
      Error::workflow_config_error("hello"),
      Error::workflow_config_error("hello")
    );
    assert_eq!(
      Error::internal_runtime_error("hello"),
      Error::internal_runtime_error("hello")
    );
    assert_eq!(
      Error::io_error(std::io::Error::new(std::io::ErrorKind::Other, "hello"),),
      Error::io_error(std::io::Error::new(std::io::ErrorKind::Other, "hello"))
    );
    assert_eq!(
      Error::unsupported_feature("hello"),
      Error::unsupported_feature("hello")
    );
    assert_eq!(Error::failed(1), Error::failed(1));
  }

  #[test]
  fn test_ne() {
    assert_ne!(
      Error::workflow_config_error("hello"),
      Error::workflow_config_error("world")
    );
    assert_ne!(
      Error::internal_runtime_error("hello"),
      Error::internal_runtime_error("world")
    );
    assert_ne!(
      Error::io_error(std::io::Error::new(std::io::ErrorKind::Other, "hello"),),
      Error::io_error(std::io::Error::new(
        std::io::ErrorKind::Unsupported,
        "world"
      ),)
    );
    assert_ne!(
      Error::unsupported_feature("hello"),
      Error::unsupported_feature("world")
    );
    assert_ne!(Error::failed(1), Error::failed(2));
    assert_ne!(Error::failed(1), Error::internal_runtime_error("hello"));
  }
}