1use std::path::PathBuf;
2use thiserror::Error;
3
4#[derive(Debug, Error)]
5pub enum FileEngineError {
6 #[error("source not found: {0:?}")]
7 SourceNotFound(PathBuf),
8
9 #[error("destination already exists: {0:?}")]
10 DestinationExists(PathBuf),
11
12 #[error("operation cancelled")]
13 Cancelled,
14
15 #[error("insufficient disk space: needed {needed} bytes, available {available} bytes")]
16 InsufficientSpace { needed: u64, available: u64 },
17
18 #[error("permission denied: {0:?}")]
19 PermissionDenied(PathBuf),
20
21 #[error("io error on {path:?}: {source}")]
22 Io {
23 path: PathBuf,
24 #[source]
25 source: std::io::Error,
26 },
27
28 #[error("could not infer compression format from destination: {0:?}")]
29 UnknownCompressFormat(PathBuf),
30
31 #[error("gzip compression requires a single file, got a directory: {0:?}")]
32 GzipRequiresFile(PathBuf),
33}
34
35pub type Result<T> = std::result::Result<T, FileEngineError>;
36
37#[allow(dead_code)]
48pub(crate) fn from_io(path: PathBuf, source: std::io::Error) -> FileEngineError {
49 match source.kind() {
50 std::io::ErrorKind::NotFound => FileEngineError::SourceNotFound(path),
51 std::io::ErrorKind::PermissionDenied => FileEngineError::PermissionDenied(path),
52 std::io::ErrorKind::StorageFull => FileEngineError::InsufficientSpace {
53 needed: 0,
54 available: 0,
55 },
56 _ => FileEngineError::Io { path, source },
57 }
58}
59
60#[cfg(feature = "diagnostics")]
61use error_engine::{EngineDiagnostic, Severity};
62
63#[cfg(feature = "diagnostics")]
64impl EngineDiagnostic for FileEngineError {
65 fn code(&self) -> &'static str {
66 match self {
67 Self::SourceNotFound(_) => "FE_SOURCE_NOT_FOUND",
68 Self::DestinationExists(_) => "FE_DEST_EXISTS",
69 Self::Cancelled => "FE_CANCELLED",
70 Self::InsufficientSpace { .. } => "FE_NO_SPACE",
71 Self::PermissionDenied(_) => "FE_PERMISSION_DENIED",
72 Self::Io { .. } => "FE_IO",
73 Self::UnknownCompressFormat(_) => "FE_UNKNOWN_COMPRESS_FORMAT",
74 Self::GzipRequiresFile(_) => "FE_GZIP_REQUIRES_FILE",
75 }
76 }
77
78 fn severity(&self) -> Severity {
79 Severity::Error
80 }
81
82 fn context(&self) -> Vec<(&'static str, String)> {
83 match self {
84 Self::SourceNotFound(p) => vec![("path", p.display().to_string())],
85 Self::DestinationExists(p) => vec![("path", p.display().to_string())],
86 Self::InsufficientSpace { needed, available } => vec![
87 ("needed", needed.to_string()),
88 ("available", available.to_string()),
89 ],
90 Self::PermissionDenied(p) => vec![("path", p.display().to_string())],
91 Self::Io { path, .. } => vec![("path", path.display().to_string())],
92 Self::Cancelled => vec![],
93 Self::UnknownCompressFormat(p) => vec![("path", p.display().to_string())],
94 Self::GzipRequiresFile(p) => vec![("path", p.display().to_string())],
95 }
96 }
97}
98
99#[cfg(feature = "diagnostics")]
107pub fn catalog() -> error_engine::Catalog {
108 error_engine::Catalog::from_str(include_str!("../errors.toml"))
109 .expect("file-engine's own catalog is valid TOML — covered by tests")
110}