Skip to main content

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}