use std::path::PathBuf;
use serde::Serialize;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum DiaryxError {
#[error("Unsupported operation: {0}")]
Unsupported(String),
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Failed to read file '{path}': {source}")]
FileRead {
path: PathBuf,
source: std::io::Error,
},
#[error("Failed to write file '{path}': {source}")]
FileWrite {
path: PathBuf,
source: std::io::Error,
},
#[error("YAML parsing error: {0}")]
Yaml(#[from] serde_yaml::Error),
#[error("YAML parsing error in '{path}': {message}")]
YamlParse {
path: PathBuf,
message: String,
},
#[error("No frontmatter found in '{0}'")]
NoFrontmatter(PathBuf),
#[error("Invalid frontmatter structure in '{0}'")]
InvalidFrontmatter(PathBuf),
#[error(
"Invalid date format: '{0}'. Try 'today', 'yesterday', 'last friday', '3 days ago', or 'YYYY-MM-DD'"
)]
InvalidDateFormat(String),
#[error("Config parse error: {0}")]
ConfigParse(#[from] toml::de::Error),
#[error("Config serialize error: {0}")]
ConfigSerialize(#[from] toml::ser::Error),
#[error("Could not determine config directory")]
NoConfigDir,
#[error("Configuration not initialized. Run 'diaryx init' first.")]
ConfigNotInitialized,
#[error("No editor found. Set $EDITOR, $VISUAL, or configure editor in config file")]
NoEditorFound,
#[error("Failed to launch editor '{editor}': {source}")]
EditorLaunchFailed {
editor: String,
source: std::io::Error,
},
#[error("Editor exited with code {0}")]
EditorExited(i32),
#[error("Workspace not found at '{0}'")]
WorkspaceNotFound(PathBuf),
#[error("Workspace already exists at '{0}'")]
WorkspaceAlreadyExists(PathBuf),
#[error("Template not found: '{0}'")]
TemplateNotFound(String),
#[error("Template already exists: '{0}'")]
TemplateAlreadyExists(PathBuf),
#[error("Invalid path '{path}': {message}")]
InvalidPath {
path: PathBuf,
message: String,
},
#[cfg(feature = "crdt")]
#[error("CRDT error: {0}")]
Crdt(String),
#[cfg(all(feature = "crdt-sqlite", not(target_arch = "wasm32")))]
#[error("Database error: {0}")]
Database(#[from] rusqlite::Error),
}
pub type Result<T> = std::result::Result<T, DiaryxError>;
#[derive(Debug, Clone, Serialize)]
pub struct SerializableError {
pub kind: String,
pub message: String,
pub path: Option<PathBuf>,
}
impl From<&DiaryxError> for SerializableError {
fn from(err: &DiaryxError) -> Self {
let kind = match err {
DiaryxError::Io(_) => "Io",
DiaryxError::FileRead { .. } => "FileRead",
DiaryxError::FileWrite { .. } => "FileWrite",
DiaryxError::Yaml(_) => "Yaml",
DiaryxError::YamlParse { .. } => "YamlParse",
DiaryxError::NoFrontmatter(_) => "NoFrontmatter",
DiaryxError::InvalidFrontmatter(_) => "InvalidFrontmatter",
DiaryxError::InvalidDateFormat(_) => "InvalidDateFormat",
DiaryxError::ConfigParse(_) => "ConfigParse",
DiaryxError::ConfigSerialize(_) => "ConfigSerialize",
DiaryxError::NoConfigDir => "NoConfigDir",
DiaryxError::ConfigNotInitialized => "ConfigNotInitialized",
DiaryxError::NoEditorFound => "NoEditorFound",
DiaryxError::EditorLaunchFailed { .. } => "EditorLaunchFailed",
DiaryxError::EditorExited(_) => "EditorExited",
DiaryxError::WorkspaceNotFound(_) => "WorkspaceNotFound",
DiaryxError::WorkspaceAlreadyExists(_) => "WorkspaceAlreadyExists",
DiaryxError::TemplateNotFound(_) => "TemplateNotFound",
DiaryxError::TemplateAlreadyExists(_) => "TemplateAlreadyExists",
DiaryxError::InvalidPath { .. } => "InvalidPath",
DiaryxError::Unsupported(_) => "Unsupported",
#[cfg(feature = "crdt")]
DiaryxError::Crdt(_) => "Crdt",
#[cfg(all(feature = "crdt-sqlite", not(target_arch = "wasm32")))]
DiaryxError::Database(_) => "Database",
}
.to_string();
let path = match err {
DiaryxError::FileRead { path, .. } => Some(path.clone()),
DiaryxError::FileWrite { path, .. } => Some(path.clone()),
DiaryxError::YamlParse { path, .. } => Some(path.clone()),
DiaryxError::NoFrontmatter(path) => Some(path.clone()),
DiaryxError::InvalidFrontmatter(path) => Some(path.clone()),
DiaryxError::WorkspaceNotFound(path) => Some(path.clone()),
DiaryxError::WorkspaceAlreadyExists(path) => Some(path.clone()),
DiaryxError::TemplateAlreadyExists(path) => Some(path.clone()),
DiaryxError::InvalidPath { path, .. } => Some(path.clone()),
_ => None,
};
Self {
kind,
message: err.to_string(),
path,
}
}
}
impl From<DiaryxError> for SerializableError {
fn from(err: DiaryxError) -> Self {
SerializableError::from(&err)
}
}
impl DiaryxError {
pub fn to_serializable(&self) -> SerializableError {
SerializableError::from(self)
}
}