proplate-errors 0.4.0

Proplate is a CLI tool that allows you to quickly create and set up project structures based on templates, and also create your own templates for your future projects.
Documentation
use owo_colors::OwoColorize;
use proplate_tui::logger::{self, AsError};

#[derive(Debug, Clone)]
pub enum TemplateErrorKind {
  NotFound { is_remote: bool },
  Invalid,
  NoConfig,
}

#[derive(Debug, Clone)]
pub enum CliErrorKind {
  Prompt,
}

#[derive(Debug, Clone)]
pub enum ProplateErrorKind {
  Cli(CliErrorKind),
  Template {
    kind: TemplateErrorKind,
    location: String,
  },
  Fs {
    concerned_paths: Vec<String>,
    operation: String,
  },
  Git {
    cmd: String,
    raw_stderr: String,
  },
}

impl ToString for ProplateErrorKind {
  fn to_string(&self) -> String {
    let str = match self {
      ProplateErrorKind::Cli(_) => "Cli",
      ProplateErrorKind::Template { .. } => "Template",
      ProplateErrorKind::Fs { .. } => "Fs",
      ProplateErrorKind::Git { .. } => "Git",
    };
    str.into()
  }
}

#[derive(Debug)]
pub struct ProplateError {
  kind: ProplateErrorKind,
  cause: Option<String>,
  ctx: Option<String>,
}

pub type ProplateResult<T> = Result<T, ProplateError>;

impl ProplateError {
  pub fn create(kind: ProplateErrorKind) -> ProplateError {
    Self {
      kind,
      cause: None,
      ctx: None,
    }
  }

  pub fn with_ctx(mut self, ctx: &str) -> Self {
    self.ctx = Some(ctx.into());
    self
  }

  pub fn with_cause(mut self, cause: &str) -> Self {
    self.cause = Some(cause.into());
    self
  }

  pub fn has_ctx(&self) -> bool {
    self.ctx.is_some()
  }

  pub fn has_cause(&self) -> bool {
    self.cause.is_some()
  }
}

impl AsError for ProplateError {
  fn print_err(&self) -> String {
    let contextual = match self.kind.clone() {
      ProplateErrorKind::Template { kind, location } => match kind {
        TemplateErrorKind::NotFound { is_remote } => {
          let location_spec = match is_remote {
            true => "remote",
            false => "",
          };
          format!("{} template '{}' cannot be found", location_spec, location)
            .trim()
            .into()
        }
        TemplateErrorKind::Invalid => {
          format!("template at '{}' config (meta.json) is not valid", location)
        }

        TemplateErrorKind::NoConfig => {
          format!("template at '{}' has no config file", location)
        }
      },

      ProplateErrorKind::Cli(kind) => match kind {
        CliErrorKind::Prompt => format!("a problem occured when prompting the user"),
      },

      ProplateErrorKind::Fs {
        concerned_paths,
        operation,
      } => format!(
        "op '{}' cannot be done\n\nConcerned paths are:\n\n{}",
        operation,
        concerned_paths
          .iter()
          .map(|p| format!("- {}", p))
          .collect::<Vec<_>>()
          .join("\n")
      ),

      ProplateErrorKind::Git { cmd, raw_stderr } => {
        format!("command '{}' failed with git err:\n\n{}", cmd, raw_stderr)
      }
    };

    let kind = format!("Error: `{}`", self.kind.to_string());
    let ctx = match self.ctx.clone() {
      Some(_ctx) => format!("\n\nCtx: {}", &_ctx.bold()),
      _ => "".into(),
    };
    let cause = match self.cause.clone() {
      Some(_cause) => format!("\n\nCause:\n{}", &_cause).red().to_string(),
      _ => "".into(),
    };

    logger::error(&format!("\n{kind}\n{contextual}{ctx}{cause}"))
  }
}