use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum ErrorCode {
InvalidPathOrName,
NotFound,
AlreadyExists,
WrongNodeType,
DirectoryNotEmpty,
LinkLoop,
BrokenLink,
WorkspaceBoundaryViolation,
PermissionDenied,
RevisionConflict,
QuotaExceeded,
InvalidRange,
InvalidCursor,
StorageBusy,
InternalStorageFailure,
}
#[derive(Debug, thiserror::Error)]
#[error("{message}")]
pub struct FsError {
code: ErrorCode,
message: String,
details: Value,
}
pub type FsResult<T> = Result<T, FsError>;
impl FsError {
pub fn new(code: ErrorCode, message: impl Into<String>, details: Value) -> Self {
Self {
code,
message: message.into(),
details,
}
}
pub fn invalid_path_or_name(subject: impl std::fmt::Display) -> Self {
Self::for_subject(
ErrorCode::InvalidPathOrName,
"invalid path or name",
subject,
)
}
pub fn not_found(subject: impl std::fmt::Display) -> Self {
Self::for_subject(ErrorCode::NotFound, "not found", subject)
}
pub fn already_exists(subject: impl std::fmt::Display) -> Self {
Self::for_subject(ErrorCode::AlreadyExists, "already exists", subject)
}
pub fn wrong_node_type(subject: impl std::fmt::Display) -> Self {
Self::for_subject(ErrorCode::WrongNodeType, "wrong node type", subject)
}
pub fn directory_not_empty(subject: impl std::fmt::Display) -> Self {
Self::for_subject(ErrorCode::DirectoryNotEmpty, "directory not empty", subject)
}
pub fn link_loop(subject: impl std::fmt::Display) -> Self {
Self::for_subject(ErrorCode::LinkLoop, "link loop", subject)
}
pub fn broken_link(subject: impl std::fmt::Display) -> Self {
Self::for_subject(ErrorCode::BrokenLink, "broken link", subject)
}
pub fn workspace_boundary_violation(subject: impl std::fmt::Display) -> Self {
Self::for_subject(
ErrorCode::WorkspaceBoundaryViolation,
"workspace boundary violation",
subject,
)
}
pub fn permission_denied(subject: impl std::fmt::Display) -> Self {
Self::for_subject(ErrorCode::PermissionDenied, "permission denied", subject)
}
pub fn revision_conflict(subject: impl std::fmt::Display) -> Self {
Self::for_subject(ErrorCode::RevisionConflict, "revision conflict", subject)
}
pub fn quota_exceeded(subject: impl std::fmt::Display) -> Self {
Self::for_subject(ErrorCode::QuotaExceeded, "quota exceeded", subject)
}
pub fn invalid_range(subject: impl std::fmt::Display) -> Self {
Self::for_subject(ErrorCode::InvalidRange, "invalid range", subject)
}
pub fn invalid_cursor(subject: impl std::fmt::Display) -> Self {
Self::for_subject(ErrorCode::InvalidCursor, "invalid cursor", subject)
}
pub fn storage_busy(subject: impl std::fmt::Display) -> Self {
Self::for_subject(ErrorCode::StorageBusy, "storage busy", subject)
}
pub fn internal_storage_failure(subject: impl std::fmt::Display) -> Self {
Self::for_subject(
ErrorCode::InternalStorageFailure,
"internal storage failure",
subject,
)
}
pub const fn code(&self) -> ErrorCode {
self.code
}
pub fn message(&self) -> &str {
&self.message
}
pub const fn details(&self) -> &Value {
&self.details
}
fn for_subject(code: ErrorCode, label: &str, subject: impl std::fmt::Display) -> Self {
Self::new(code, format!("{label}: {subject}"), Value::Null)
}
}