java_manager/errors.rs
1// Copyright 2026 TaimWay
2//
3// @file: errors.rs
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17use std::error::Error;
18use std::{fmt, result};
19
20use glob;
21
22/// Result type alias for Java locator operations.
23///
24/// This is a convenience type alias for `Result<T, JavaLocatorError>`.
25///
26/// # Examples
27///
28/// ```rust
29/// use java_manager;
30///
31/// fn locate_java() -> java_manager::Result<String> {
32/// java_manager::locate_java_home()
33/// }
34/// ```
35pub type Result<T> = result::Result<T, JavaLocatorError>;
36
37/// Error type for Java locator operations.
38///
39/// This error type encapsulates various errors that can occur
40/// when locating or working with Java installations.
41///
42/// # Examples
43///
44/// ```rust
45/// use java_manager::JavaLocatorError;
46///
47/// let error = JavaLocatorError::new("Java not found".to_string());
48/// println!("Error: {}", error);
49/// ```
50#[derive(Debug)]
51pub struct JavaLocatorError {
52 /// Human-readable error description
53 description: String,
54}
55
56impl JavaLocatorError {
57 /// Creates a new `JavaLocatorError` with the given description.
58 ///
59 /// # Arguments
60 ///
61 /// * `description` - Error description
62 ///
63 /// # Returns
64 ///
65 /// A new `JavaLocatorError` instance
66 ///
67 /// # Examples
68 ///
69 /// ```rust
70 /// use java_manager::JavaLocatorError;
71 ///
72 /// let error = JavaLocatorError::new("Failed to locate Java".to_string());
73 /// ```
74 pub(crate) fn new(description: String) -> JavaLocatorError {
75 JavaLocatorError { description }
76 }
77
78 /// Returns the error description.
79 ///
80 /// # Returns
81 ///
82 /// Error description string
83 ///
84 /// # Examples
85 ///
86 /// ```rust
87 /// use java_manager::JavaLocatorError;
88 ///
89 /// let error = JavaLocatorError::new("Test error".to_string());
90 /// assert_eq!(error.description(), "Test error");
91 /// ```
92 pub fn description(&self) -> &str {
93 self.description.as_str()
94 }
95
96 /// Creates an error indicating Java is not installed or not in PATH.
97 ///
98 /// # Returns
99 ///
100 /// A `JavaLocatorError` with appropriate message
101 ///
102 /// # Examples
103 ///
104 /// ```rust
105 /// use java_manager::JavaLocatorError;
106 ///
107 /// let error = JavaLocatorError::java_not_found();
108 /// ```
109 pub fn java_not_found() -> Self {
110 JavaLocatorError::new(
111 "Java is not installed or not in the system PATH".to_string()
112 )
113 }
114
115 /// Creates an error indicating a file was not found in the Java installation.
116 ///
117 /// # Arguments
118 ///
119 /// * `file_name` - Name of the file that was not found
120 /// * `java_home` - Java home directory where the file was searched
121 ///
122 /// # Returns
123 ///
124 /// A `JavaLocatorError` with appropriate message
125 ///
126 /// # Examples
127 ///
128 /// ```rust
129 /// use java_manager::JavaLocatorError;
130 ///
131 /// let error = JavaLocatorError::file_not_found("libjsig.so", "/usr/lib/jvm/java-11");
132 /// ```
133 pub fn file_not_found(file_name: &str, java_home: &str) -> Self {
134 JavaLocatorError::new(
135 format!(
136 "Could not find '{}' in any subdirectory of {}",
137 file_name, java_home
138 )
139 )
140 }
141
142 /// Creates an error indicating a command execution failure.
143 ///
144 /// # Arguments
145 ///
146 /// * `command` - Command that failed
147 /// * `error` - Underlying error
148 ///
149 /// # Returns
150 ///
151 /// A `JavaLocatorError` with appropriate message
152 ///
153 /// # Examples
154 ///
155 /// ```rust
156 /// use java_manager::JavaLocatorError;
157 ///
158 /// let error = JavaLocatorError::command_failed("java -version", "Permission denied");
159 /// ```
160 pub fn command_failed(command: &str, error: &str) -> Self {
161 JavaLocatorError::new(
162 format!("Failed to execute command '{}': {}", command, error)
163 )
164 }
165
166 /// Creates an error indicating an invalid Java installation.
167 ///
168 /// # Arguments
169 ///
170 /// * `path` - Path to the invalid Java installation
171 /// * `reason` - Reason why the installation is invalid
172 ///
173 /// # Returns
174 ///
175 /// A `JavaLocatorError` with appropriate message
176 ///
177 /// # Examples
178 ///
179 /// ```rust
180 /// use java_manager::JavaLocatorError;
181 ///
182 /// let error = JavaLocatorError::invalid_installation("/invalid/path", "Executable not found");
183 /// ```
184 pub fn invalid_installation(path: &str, reason: &str) -> Self {
185 JavaLocatorError::new(
186 format!("Invalid Java installation at '{}': {}", path, reason)
187 )
188 }
189
190 /// Creates an error indicating an invalid UTF-8 sequence in a path.
191 ///
192 /// # Arguments
193 ///
194 /// * `path` - Path that contains invalid UTF-8
195 ///
196 /// # Returns
197 ///
198 /// A `JavaLocatorError` with appropriate message
199 ///
200 /// # Examples
201 ///
202 /// ```rust
203 /// use java_manager::JavaLocatorError;
204 ///
205 /// let error = JavaLocatorError::invalid_utf8_path("<invalid-utf8-path>");
206 /// ```
207 pub fn invalid_utf8_path(path: &str) -> Self {
208 JavaLocatorError::new(
209 format!("Path contains invalid UTF-8: {}", path)
210 )
211 }
212}
213
214impl fmt::Display for JavaLocatorError {
215 /// Formats the error for display.
216 ///
217 /// # Arguments
218 ///
219 /// * `f` - Formatter
220 ///
221 /// # Returns
222 ///
223 /// Formatter result
224 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
225 write!(f, "JavaLocatorError: {}", self.description)
226 }
227}
228
229impl Error for JavaLocatorError {
230 /// Returns the error description (for compatibility with std::error::Error).
231 ///
232 /// # Returns
233 ///
234 /// Error description
235 fn description(&self) -> &str {
236 self.description.as_str()
237 }
238
239 /// Returns the cause of the error (if any).
240 ///
241 /// # Returns
242 ///
243 /// `None` (this error doesn't wrap other errors)
244 fn cause(&self) -> Option<&dyn Error> {
245 None
246 }
247
248 /// Provides source of the error (for compatibility with Rust 1.30+).
249 ///
250 /// # Returns
251 ///
252 /// `None` (this error doesn't wrap other errors)
253 fn source(&self) -> Option<&(dyn Error + 'static)> {
254 None
255 }
256}
257
258impl From<std::io::Error> for JavaLocatorError {
259 /// Converts a `std::io::Error` to a `JavaLocatorError`.
260 ///
261 /// # Arguments
262 ///
263 /// * `err` - IO error to convert
264 ///
265 /// # Returns
266 ///
267 /// A `JavaLocatorError` with the IO error description
268 fn from(err: std::io::Error) -> JavaLocatorError {
269 JavaLocatorError::new(format!("IO error: {}", err))
270 }
271}
272
273impl From<std::str::Utf8Error> for JavaLocatorError {
274 /// Converts a `std::str::Utf8Error` to a `JavaLocatorError`.
275 ///
276 /// # Arguments
277 ///
278 /// * `err` - UTF-8 error to convert
279 ///
280 /// # Returns
281 ///
282 /// A `JavaLocatorError` with the UTF-8 error description
283 fn from(err: std::str::Utf8Error) -> JavaLocatorError {
284 JavaLocatorError::new(format!("UTF-8 error: {}", err))
285 }
286}
287
288impl From<glob::PatternError> for JavaLocatorError {
289 /// Converts a `glob::PatternError` to a `JavaLocatorError`.
290 ///
291 /// # Arguments
292 ///
293 /// * `err` - Glob pattern error to convert
294 ///
295 /// # Returns
296 ///
297 /// A `JavaLocatorError` with the glob error description
298 fn from(err: glob::PatternError) -> JavaLocatorError {
299 JavaLocatorError::new(format!("Glob pattern error: {}", err))
300 }
301}
302
303#[cfg(test)]
304mod tests {
305 use super::*;
306
307 /// Tests creating a new JavaLocatorError
308 #[test]
309 fn test_new_error() {
310 let error = JavaLocatorError::new("Test error".to_string());
311 assert_eq!(error.description(), "Test error");
312 }
313
314 /// Tests the Display trait implementation
315 #[test]
316 fn test_display() {
317 let error = JavaLocatorError::new("Test error message".to_string());
318 let display_output = format!("{}", error);
319 assert!(display_output.contains("JavaLocatorError"));
320 assert!(display_output.contains("Test error message"));
321 }
322
323 /// Tests the Error trait implementation
324 #[test]
325 fn test_error_trait() {
326 let error = JavaLocatorError::new("Test error".to_string());
327
328 assert_eq!(error.description(), "Test error");
329 assert!(error.source().is_none());
330 assert!(error.cause().is_none());
331 }
332
333 /// Tests the java_not_found helper method
334 #[test]
335 fn test_java_not_found() {
336 let error = JavaLocatorError::java_not_found();
337 assert_eq!(error.description(), "Java is not installed or not in the system PATH");
338 }
339
340 /// Tests the file_not_found helper method
341 #[test]
342 fn test_file_not_found() {
343 let error = JavaLocatorError::file_not_found("libjsig.so", "/usr/lib/jvm/java-11");
344 let description = error.description();
345 assert!(description.contains("libjsig.so"));
346 assert!(description.contains("/usr/lib/jvm/java-11"));
347 }
348
349 /// Tests the command_failed helper method
350 #[test]
351 fn test_command_failed() {
352 let error = JavaLocatorError::command_failed("java -version", "Permission denied");
353 let description = error.description();
354 assert!(description.contains("java -version"));
355 assert!(description.contains("Permission denied"));
356 }
357
358 /// Tests the invalid_installation helper method
359 #[test]
360 fn test_invalid_installation() {
361 let error = JavaLocatorError::invalid_installation("/invalid/path", "Executable not found");
362 let description = error.description();
363 assert!(description.contains("/invalid/path"));
364 assert!(description.contains("Executable not found"));
365 }
366
367 /// Tests the invalid_utf8_path helper method
368 #[test]
369 fn test_invalid_utf8_path() {
370 let error = JavaLocatorError::invalid_utf8_path("<invalid-utf8-path>");
371 let description = error.description();
372 assert!(description.contains("<invalid-utf8-path>"));
373 }
374
375 /// Tests conversion from std::io::Error
376 #[test]
377 fn test_from_io_error() {
378 let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "File not found");
379 let java_error: JavaLocatorError = io_error.into();
380
381 let description = java_error.description();
382 assert!(description.contains("IO error"));
383 assert!(description.contains("File not found"));
384 }
385
386 /// Tests conversion from std::str::Utf8Error
387 #[test]
388 fn test_from_utf8_error() {
389 // Create an invalid UTF-8 sequence
390 let invalid_utf8: &[u8] = &[0xff, 0xff, 0xff];
391 let utf8_error = std::str::from_utf8(invalid_utf8).unwrap_err();
392
393 let java_error: JavaLocatorError = utf8_error.into();
394 let description = java_error.description();
395 assert!(description.contains("UTF-8 error"));
396 }
397
398 /// Tests conversion from glob::PatternError
399 #[test]
400 fn test_from_glob_error() {
401 // Create an invalid glob pattern
402 let pattern_result = glob::Pattern::new("**[invalid");
403 assert!(pattern_result.is_err());
404
405 if let Err(glob_error) = pattern_result {
406 let java_error: JavaLocatorError = glob_error.into();
407 let description = java_error.description();
408 assert!(description.contains("Glob pattern error"));
409 }
410 }
411
412 /// Tests the Result type alias
413 #[test]
414 fn test_result_type() {
415 // Test Ok variant
416 let ok_result: Result<String> = Ok("Success".to_string());
417 assert!(ok_result.is_ok());
418 assert_eq!(ok_result.unwrap(), "Success");
419
420 // Test Err variant
421 let err_result: Result<String> = Err(JavaLocatorError::new("Error".to_string()));
422 assert!(err_result.is_err());
423
424 if let Err(e) = err_result {
425 assert_eq!(e.description(), "Error");
426 }
427 }
428
429 /// Tests error chaining (source method)
430 #[test]
431 fn test_error_source() {
432 let error = JavaLocatorError::new("Wrapper error".to_string());
433 // JavaLocatorError doesn't wrap other errors, so source should be None
434 assert!(error.source().is_none());
435 }
436}