Skip to main content

deps_core/
error.rs

1use thiserror::Error;
2
3/// Core error types for deps-lsp.
4///
5/// Extended from Phase 1 to support multiple ecosystems (Cargo, npm, PyPI).
6/// All errors provide structured error handling with source error tracking.
7///
8/// # Examples
9///
10/// ```
11/// use deps_core::error::{DepsError, Result};
12///
13/// fn parse_file(content: &str, file_type: &str) -> Result<()> {
14///     // Parsing errors are automatically wrapped
15///     if content.is_empty() {
16///         return Err(DepsError::ParseError {
17///             file_type: file_type.into(),
18///             source: Box::new(std::io::Error::new(
19///                 std::io::ErrorKind::InvalidData,
20///                 "empty content"
21///             )),
22///         });
23///     }
24///     Ok(())
25/// }
26/// ```
27#[derive(Error, Debug)]
28pub enum DepsError {
29    #[error("failed to parse {file_type}: {source}")]
30    ParseError {
31        file_type: String,
32        #[source]
33        source: Box<dyn std::error::Error + Send + Sync>,
34    },
35
36    #[error("registry request failed for {package}: {source}")]
37    RegistryError {
38        package: String,
39        #[source]
40        source: reqwest::Error,
41    },
42
43    #[error("cache error: {0}")]
44    CacheError(String),
45
46    #[error("invalid version requirement: {0}")]
47    InvalidVersionReq(String),
48
49    #[error("I/O error: {0}")]
50    Io(#[from] std::io::Error),
51
52    #[error("JSON error: {0}")]
53    Json(#[from] serde_json::Error),
54
55    #[error("unsupported ecosystem: {0}")]
56    UnsupportedEcosystem(String),
57
58    #[error("ambiguous ecosystem detection for file: {0}")]
59    AmbiguousEcosystem(String),
60
61    #[error("invalid URI: {0}")]
62    InvalidUri(String),
63}
64
65/// Convenience type alias for `Result<T, DepsError>`.
66///
67/// This is the standard `Result` type used throughout the deps-lsp codebase.
68/// It simplifies function signatures by defaulting the error type to `DepsError`.
69///
70/// # Examples
71///
72/// ```
73/// use deps_core::error::Result;
74///
75/// fn get_version(name: &str) -> Result<String> {
76///     if name.is_empty() {
77///         return Err(deps_core::error::DepsError::CacheError("empty name".into()));
78///     }
79///     Ok("1.0.0".into())
80/// }
81/// ```
82pub type Result<T> = std::result::Result<T, DepsError>;
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87
88    #[test]
89    fn test_error_display() {
90        let error = DepsError::CacheError("test error".into());
91        assert_eq!(error.to_string(), "cache error: test error");
92    }
93
94    #[test]
95    fn test_invalid_version_req() {
96        let error = DepsError::InvalidVersionReq("invalid".into());
97        assert_eq!(error.to_string(), "invalid version requirement: invalid");
98    }
99
100    #[test]
101    fn test_parse_error() {
102        let io_err = std::io::Error::new(std::io::ErrorKind::InvalidData, "bad data");
103        let error = DepsError::ParseError {
104            file_type: "Cargo.toml".into(),
105            source: Box::new(io_err),
106        };
107        assert!(error.to_string().contains("failed to parse Cargo.toml"));
108    }
109
110    #[test]
111    fn test_io_error_conversion() {
112        let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
113        let error: DepsError = io_err.into();
114        assert!(error.to_string().contains("I/O error"));
115    }
116
117    #[test]
118    fn test_unsupported_ecosystem() {
119        let error = DepsError::UnsupportedEcosystem("unknown".into());
120        assert_eq!(error.to_string(), "unsupported ecosystem: unknown");
121    }
122
123    #[test]
124    fn test_ambiguous_ecosystem() {
125        let error = DepsError::AmbiguousEcosystem("file.txt".into());
126        assert_eq!(
127            error.to_string(),
128            "ambiguous ecosystem detection for file: file.txt"
129        );
130    }
131
132    #[test]
133    fn test_invalid_uri() {
134        let error = DepsError::InvalidUri("http://example.com".into());
135        assert_eq!(error.to_string(), "invalid URI: http://example.com");
136    }
137}