gravityfile_core/
error.rs1use std::path::PathBuf;
4
5use serde::{Deserialize, Serialize};
6use thiserror::Error;
7
8#[derive(Debug, Error)]
10pub enum ScanError {
11 #[error("Permission denied: {path}")]
13 PermissionDenied { path: PathBuf },
14
15 #[error("Path not found: {path}")]
17 NotFound { path: PathBuf },
18
19 #[error("I/O error at {path}: {source}")]
21 Io {
22 path: PathBuf,
23 #[source]
24 source: std::io::Error,
25 },
26
27 #[error("Operation interrupted")]
29 Interrupted,
30
31 #[error("Too many errors ({count}), aborting")]
33 TooManyErrors { count: usize },
34
35 #[error("Invalid configuration: {message}")]
37 InvalidConfig { message: String },
38
39 #[error("Root path is not a directory: {path}")]
41 NotADirectory { path: PathBuf },
42
43 #[error("{message}")]
45 Other { message: String },
46}
47
48impl ScanError {
49 pub fn io(path: impl Into<PathBuf>, source: std::io::Error) -> Self {
51 let path = path.into();
52 match source.kind() {
53 std::io::ErrorKind::PermissionDenied => Self::PermissionDenied { path },
54 std::io::ErrorKind::NotFound => Self::NotFound { path },
55 _ => Self::Io { path, source },
56 }
57 }
58}
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
62pub enum WarningKind {
63 PermissionDenied,
65 BrokenSymlink,
67 ReadError,
69 MetadataError,
71 CrossFilesystem,
73}
74
75#[derive(Debug, Clone, Serialize, Deserialize)]
77pub struct ScanWarning {
78 pub path: PathBuf,
80 pub message: String,
82 pub kind: WarningKind,
84}
85
86impl ScanWarning {
87 pub fn new(path: impl Into<PathBuf>, message: impl Into<String>, kind: WarningKind) -> Self {
89 Self {
90 path: path.into(),
91 message: message.into(),
92 kind,
93 }
94 }
95
96 pub fn permission_denied(path: impl Into<PathBuf>) -> Self {
98 let path = path.into();
99 Self {
100 message: format!("Permission denied: {}", path.display()),
101 path,
102 kind: WarningKind::PermissionDenied,
103 }
104 }
105
106 pub fn broken_symlink(path: impl Into<PathBuf>, target: &str) -> Self {
108 let path = path.into();
109 Self {
110 message: format!("Broken symlink: {} -> {target}", path.display()),
111 path,
112 kind: WarningKind::BrokenSymlink,
113 }
114 }
115
116 pub fn read_error(path: impl Into<PathBuf>, error: &std::io::Error) -> Self {
118 let path = path.into();
119 Self {
120 message: format!("Read error: {error}"),
121 path,
122 kind: WarningKind::ReadError,
123 }
124 }
125}
126
127#[cfg(test)]
128mod tests {
129 use super::*;
130
131 #[test]
132 fn test_scan_error_io() {
133 let err = ScanError::io(
134 "/test/path",
135 std::io::Error::new(std::io::ErrorKind::PermissionDenied, "denied"),
136 );
137 assert!(matches!(err, ScanError::PermissionDenied { .. }));
138 }
139
140 #[test]
141 fn test_scan_warning_creation() {
142 let warning = ScanWarning::permission_denied("/test/path");
143 assert_eq!(warning.kind, WarningKind::PermissionDenied);
144 assert!(warning.message.contains("Permission denied"));
145 }
146}