proplate_errors/
lib.rs

1use owo_colors::OwoColorize;
2use proplate_tui::logger::{self, AsError};
3
4#[derive(Debug, Clone)]
5pub enum TemplateErrorKind {
6  NotFound { is_remote: bool },
7  Invalid,
8  NoConfig,
9}
10
11#[derive(Debug, Clone)]
12pub enum CliErrorKind {
13  Prompt,
14}
15
16#[derive(Debug, Clone)]
17pub enum ProplateErrorKind {
18  Cli(CliErrorKind),
19  Template {
20    kind: TemplateErrorKind,
21    location: String,
22  },
23  Fs {
24    concerned_paths: Vec<String>,
25    operation: String,
26  },
27  Git {
28    cmd: String,
29    raw_stderr: String,
30  },
31}
32
33impl ToString for ProplateErrorKind {
34  fn to_string(&self) -> String {
35    let str = match self {
36      ProplateErrorKind::Cli(_) => "Cli",
37      ProplateErrorKind::Template { .. } => "Template",
38      ProplateErrorKind::Fs { .. } => "Fs",
39      ProplateErrorKind::Git { .. } => "Git",
40    };
41    str.into()
42  }
43}
44
45#[derive(Debug)]
46pub struct ProplateError {
47  kind: ProplateErrorKind,
48  cause: Option<String>,
49  ctx: Option<String>,
50}
51
52pub type ProplateResult<T> = Result<T, ProplateError>;
53
54impl ProplateError {
55  pub fn create(kind: ProplateErrorKind) -> ProplateError {
56    Self {
57      kind,
58      cause: None,
59      ctx: None,
60    }
61  }
62
63  pub fn with_ctx(mut self, ctx: &str) -> Self {
64    self.ctx = Some(ctx.into());
65    self
66  }
67
68  pub fn with_cause(mut self, cause: &str) -> Self {
69    self.cause = Some(cause.into());
70    self
71  }
72
73  pub fn has_ctx(&self) -> bool {
74    self.ctx.is_some()
75  }
76
77  pub fn has_cause(&self) -> bool {
78    self.cause.is_some()
79  }
80}
81
82impl AsError for ProplateError {
83  fn print_err(&self) -> String {
84    let contextual = match self.kind.clone() {
85      ProplateErrorKind::Template { kind, location } => match kind {
86        TemplateErrorKind::NotFound { is_remote } => {
87          let location_spec = match is_remote {
88            true => "remote",
89            false => "",
90          };
91          format!("{} template '{}' cannot be found", location_spec, location)
92            .trim()
93            .into()
94        }
95        TemplateErrorKind::Invalid => {
96          format!("template at '{}' config (meta.json) is not valid", location)
97        }
98
99        TemplateErrorKind::NoConfig => {
100          format!("template at '{}' has no config file", location)
101        }
102      },
103
104      ProplateErrorKind::Cli(kind) => match kind {
105        CliErrorKind::Prompt => format!("a problem occured when prompting the user"),
106      },
107
108      ProplateErrorKind::Fs {
109        concerned_paths,
110        operation,
111      } => format!(
112        "op '{}' cannot be done\n\nConcerned paths are:\n\n{}",
113        operation,
114        concerned_paths
115          .iter()
116          .map(|p| format!("- {}", p))
117          .collect::<Vec<_>>()
118          .join("\n")
119      ),
120
121      ProplateErrorKind::Git { cmd, raw_stderr } => {
122        format!("command '{}' failed with git err:\n\n{}", cmd, raw_stderr)
123      }
124    };
125
126    let kind = format!("Error: `{}`", self.kind.to_string());
127    let ctx = match self.ctx.clone() {
128      Some(_ctx) => format!("\n\nCtx: {}", &_ctx.bold()),
129      _ => "".into(),
130    };
131    let cause = match self.cause.clone() {
132      Some(_cause) => format!("\n\nCause:\n{}", &_cause).red().to_string(),
133      _ => "".into(),
134    };
135
136    logger::error(&format!("\n{kind}\n{contextual}{ctx}{cause}"))
137  }
138}