1use std::io;
4use thiserror::Error;
5
6#[derive(Error, Debug)]
8pub enum SortError {
9 #[error("I/O error: {0}")]
10 Io(#[from] io::Error),
11
12 #[error("Permission denied: {file}")]
13 PermissionDenied { file: String },
14
15 #[error("No such file or directory: {file}")]
16 FileNotFound { file: String },
17
18 #[error("Is a directory: {file}")]
19 IsDirectory { file: String },
20
21 #[error("Invalid key specification: {spec}")]
22 InvalidKeySpec { spec: String },
23
24 #[error("Invalid field separator: {sep}")]
25 InvalidFieldSeparator { sep: String },
26
27 #[error("Invalid buffer size: {size}")]
28 InvalidBufferSize { size: String },
29
30 #[error("Conflicting sort options: {message}")]
31 ConflictingOptions { message: String },
32
33 #[error("Memory allocation failed")]
34 OutOfMemory,
35
36 #[error("Input is not sorted at line {line}")]
37 NotSorted { line: usize },
38
39 #[error("Merge operation failed: {message}")]
40 MergeFailed { message: String },
41
42 #[error("Thread pool error: {message}")]
43 ThreadPoolError { message: String },
44
45 #[error("UTF-8 encoding error: {0}")]
46 Utf8Error(#[from] std::string::FromUtf8Error),
47
48 #[error("Parse error: {message}")]
49 ParseError { message: String },
50
51 #[error("Internal error: {message}")]
52 Internal { message: String },
53}
54
55impl SortError {
56 pub fn exit_code(&self) -> i32 {
58 match self {
59 SortError::PermissionDenied { .. }
60 | SortError::FileNotFound { .. }
61 | SortError::IsDirectory { .. }
62 | SortError::Io(_) => crate::SORT_FAILURE,
63
64 SortError::NotSorted { .. } => crate::EXIT_FAILURE,
65
66 _ => crate::EXIT_FAILURE,
67 }
68 }
69
70 pub fn permission_denied(file: &str) -> Self {
72 SortError::PermissionDenied {
73 file: file.to_string(),
74 }
75 }
76
77 pub fn file_not_found(file: &str) -> Self {
79 SortError::FileNotFound {
80 file: file.to_string(),
81 }
82 }
83
84 pub fn is_directory(file: &str) -> Self {
86 SortError::IsDirectory {
87 file: file.to_string(),
88 }
89 }
90
91 pub fn invalid_key_spec(spec: &str) -> Self {
93 SortError::InvalidKeySpec {
94 spec: spec.to_string(),
95 }
96 }
97
98 pub fn invalid_field_separator(sep: &str) -> Self {
100 SortError::InvalidFieldSeparator {
101 sep: sep.to_string(),
102 }
103 }
104
105 pub fn invalid_buffer_size(size: &str) -> Self {
107 SortError::InvalidBufferSize {
108 size: size.to_string(),
109 }
110 }
111
112 pub fn conflicting_options(message: &str) -> Self {
114 SortError::ConflictingOptions {
115 message: message.to_string(),
116 }
117 }
118
119 pub fn not_sorted(line: usize) -> Self {
121 SortError::NotSorted { line }
122 }
123
124 pub fn merge_failed(message: &str) -> Self {
126 SortError::MergeFailed {
127 message: message.to_string(),
128 }
129 }
130
131 pub fn thread_pool_error(message: &str) -> Self {
133 SortError::ThreadPoolError {
134 message: message.to_string(),
135 }
136 }
137
138 pub fn parse_error(message: &str) -> Self {
140 SortError::ParseError {
141 message: message.to_string(),
142 }
143 }
144
145 pub fn internal(message: &str) -> Self {
147 SortError::Internal {
148 message: message.to_string(),
149 }
150 }
151}
152
153pub type SortResult<T> = Result<T, SortError>;
156
157pub trait SortContext<T> {
159 fn with_context<F>(self, f: F) -> SortResult<T>
160 where
161 F: FnOnce() -> String;
162
163 fn with_file_context(self, filename: &str) -> SortResult<T>;
164}
165
166impl<T> SortContext<T> for SortResult<T> {
167 fn with_context<F>(self, f: F) -> SortResult<T>
168 where
169 F: FnOnce() -> String,
170 {
171 self.map_err(|err| match err {
172 SortError::Io(io_err) => SortError::Io(io::Error::new(
173 io_err.kind(),
174 format!("{}: {}", f(), io_err),
175 )),
176 other => other,
177 })
178 }
179
180 fn with_file_context(self, filename: &str) -> SortResult<T> {
181 self.map_err(|err| match err {
182 SortError::Io(io_err) => match io_err.kind() {
183 io::ErrorKind::PermissionDenied => SortError::permission_denied(filename),
184 io::ErrorKind::NotFound => SortError::file_not_found(filename),
185 _ => SortError::Io(io::Error::new(
186 io_err.kind(),
187 format!("{filename}: {io_err}"),
188 )),
189 },
190 other => other,
191 })
192 }
193}
194
195impl<T> SortContext<T> for Result<T, io::Error> {
196 fn with_context<F>(self, f: F) -> SortResult<T>
197 where
198 F: FnOnce() -> String,
199 {
200 self.map_err(|io_err| {
201 SortError::Io(io::Error::new(io_err.kind(), format!("{}: {io_err}", f())))
202 })
203 }
204
205 fn with_file_context(self, filename: &str) -> SortResult<T> {
206 self.map_err(|io_err| match io_err.kind() {
207 io::ErrorKind::PermissionDenied => SortError::permission_denied(filename),
208 io::ErrorKind::NotFound => SortError::file_not_found(filename),
209 _ => SortError::Io(io::Error::new(
210 io_err.kind(),
211 format!("{filename}: {io_err}"),
212 )),
213 })
214 }
215}