#[derive(Debug, thiserror::Error)]
#[error("store backend lock was poisoned by a panicked holder")]
pub(crate) struct StorePoisoned;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum PathReason {
Empty,
Absolute,
Traversal,
Control,
EmptySegment,
Backslash,
ReservedName,
UnsafeSuffix,
TooLong,
}
impl std::fmt::Display for PathReason {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let text = match self {
PathReason::Empty => "path is empty",
PathReason::Absolute => "path is absolute",
PathReason::Traversal => "path contains a traversal segment",
PathReason::Control => "path contains a control character",
PathReason::EmptySegment => "path contains an empty segment",
PathReason::Backslash => "path contains a backslash",
PathReason::ReservedName => "path contains a reserved device name",
PathReason::UnsafeSuffix => "path segment ends in an unsafe character",
PathReason::TooLong => "path is too long",
};
formatter.write_str(text)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum StoreErrorKind {
NotFound,
Anchor,
InvalidAnchor,
InvalidPath,
InvalidPattern,
Backend,
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum StoreError {
#[error("file not found: {path}")]
#[non_exhaustive]
NotFound {
path: String,
},
#[error("invalid anchor for {path}: {reason}")]
#[non_exhaustive]
InvalidAnchor {
path: String,
reason: &'static str,
},
#[error("anchor not found in {path}")]
#[non_exhaustive]
AnchorNotFound {
path: String,
anchor: String,
},
#[error("anchor occurs {count} times in {path}, expected exactly one")]
#[non_exhaustive]
AnchorAmbiguous {
path: String,
anchor: String,
count: usize,
},
#[error("invalid path {path:?}: {reason}")]
#[non_exhaustive]
InvalidPath {
path: String,
reason: PathReason,
},
#[error("invalid glob pattern {pattern:?}: {reason}")]
#[non_exhaustive]
InvalidPattern {
pattern: String,
reason: String,
},
#[error("store backend failure")]
#[non_exhaustive]
Backend {
#[source]
source: Box<dyn std::error::Error + Send + Sync>,
},
}
impl StoreError {
#[must_use]
pub fn kind(&self) -> StoreErrorKind {
match self {
StoreError::NotFound { .. } => StoreErrorKind::NotFound,
StoreError::InvalidAnchor { .. } => StoreErrorKind::InvalidAnchor,
StoreError::AnchorNotFound { .. } | StoreError::AnchorAmbiguous { .. } => {
StoreErrorKind::Anchor
}
StoreError::InvalidPath { .. } => StoreErrorKind::InvalidPath,
StoreError::InvalidPattern { .. } => StoreErrorKind::InvalidPattern,
StoreError::Backend { .. } => StoreErrorKind::Backend,
}
}
#[must_use]
pub fn is_not_found(&self) -> bool {
matches!(self, StoreError::NotFound { .. })
}
#[must_use]
pub fn path(&self) -> Option<&str> {
match self {
StoreError::NotFound { path }
| StoreError::InvalidAnchor { path, .. }
| StoreError::AnchorNotFound { path, .. }
| StoreError::AnchorAmbiguous { path, .. }
| StoreError::InvalidPath { path, .. } => Some(path),
StoreError::InvalidPattern { .. } | StoreError::Backend { .. } => None,
}
}
#[must_use]
pub fn backend(source: impl std::error::Error + Send + Sync + 'static) -> StoreError {
StoreError::Backend {
source: Box::new(source),
}
}
}