use std::{ fmt::{Display, Formatter, Result as FmtResult}, io, }; use thiserror::Error; use crate::FolgezettelId; #[derive(Debug, Error)] pub enum CliError { #[error("{0}")] Io(#[from] io::Error), #[error("failed to write: {0}")] Csv(#[from] csv::Error), #[error("failed to walk current directory: {0}")] Walk(#[from] walkdir::Error), #[error("path is not valid UTF-8: {0}")] NonUtf8Path(String), #[error("invalid note id: {0}")] InvalidId(#[from] ParseIdError), } pub type Result = std::result::Result; #[derive(Debug, Clone, PartialEq, Eq, Error)] pub enum ParseIdError { #[error("id is empty")] Empty, #[error("expected a number segment at position {position}")] ExpectedNumber { position: usize }, #[error("expected a letter segment at position {position}")] ExpectedLetter { position: usize }, #[error("number segment at position {position} exceeds 64 bits")] NumberOverflow { position: usize }, } #[derive(Debug, Clone, PartialEq, Eq, Error)] pub enum ParseFileNameError { #[error("file name has no extension")] MissingFileExtension, #[error("file name has no stem")] MissingStem, #[error("file name must contain '-'")] MissingTitleSeparator, #[error("file name title is empty")] EmptyTitle, #[error("invalid id: {0}")] InvalidId(#[from] ParseIdError), } #[derive(Debug, Clone, PartialEq, Eq, Error)] pub enum Diagnostic { #[error("non-zettel file '{file}': {error}")] NonZettelFile { file: String, #[source] error: ParseFileNameError, }, #[error("invalid file name '{file}': {error}")] InvalidFileName { file: String, #[source] error: ParseFileNameError, }, #[error("duplicate id '{id}' on {}", .files.join(", "))] DuplicateId { id: FolgezettelId, files: Vec<String>, }, #[error("missing parent '{parent}' for {file}")] MissingParent { id: FolgezettelId, parent: FolgezettelId, file: String, }, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum DiagnosticSeverity { Error, Info, Warning, } impl Display for DiagnosticSeverity { fn fmt(&self, formatter: &mut Formatter<'_>) -> FmtResult { formatter.write_str(match self { DiagnosticSeverity::Info => "info", DiagnosticSeverity::Warning => "warning", DiagnosticSeverity::Error => "error", }) } } impl Diagnostic { pub fn severity(&self) -> DiagnosticSeverity { match self { Diagnostic::InvalidFileName { error: ParseFileNameError::MissingTitleSeparator | ParseFileNameError::EmptyTitle, .. } | Diagnostic::MissingParent { .. } => DiagnosticSeverity::Warning, Diagnostic::InvalidFileName { .. } | Diagnostic::DuplicateId { .. } => { DiagnosticSeverity::Error } Diagnostic::NonZettelFile { .. } => DiagnosticSeverity::Info, } } }