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
62/// Convenience type alias for `Result<T, DepsError>`.
63///
64/// This is the standard `Result` type used throughout the deps-lsp codebase.
65/// It simplifies function signatures by defaulting the error type to `DepsError`.
66///
67/// # Examples
68///
69/// ```
70/// use deps_core::error::Result;
71///
72/// fn get_version(name: &str) -> Result<String> {
73///     if name.is_empty() {
74///         return Err(deps_core::error::DepsError::CacheError("empty name".into()));
75///     }
76///     Ok("1.0.0".into())
77/// }
78/// ```
79pub type Result<T> = std::result::Result<T, DepsError>;
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84
85    #[test]
86    fn test_error_display() {
87        let error = DepsError::CacheError("test error".into());
88        assert_eq!(error.to_string(), "cache error: test error");
89    }
90
91    #[test]
92    fn test_invalid_version_req() {
93        let error = DepsError::InvalidVersionReq("invalid".into());
94        assert_eq!(error.to_string(), "invalid version requirement: invalid");
95    }
96
97    #[test]
98    fn test_parse_error() {
99        let io_err = std::io::Error::new(std::io::ErrorKind::InvalidData, "bad data");
100        let error = DepsError::ParseError {
101            file_type: "Cargo.toml".into(),
102            source: Box::new(io_err),
103        };
104        assert!(error.to_string().contains("failed to parse Cargo.toml"));
105    }
106
107    #[test]
108    fn test_io_error_conversion() {
109        let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
110        let error: DepsError = io_err.into();
111        assert!(error.to_string().contains("I/O error"));
112    }
113
114    #[test]
115    fn test_unsupported_ecosystem() {
116        let error = DepsError::UnsupportedEcosystem("unknown".into());
117        assert_eq!(error.to_string(), "unsupported ecosystem: unknown");
118    }
119
120    #[test]
121    fn test_ambiguous_ecosystem() {
122        let error = DepsError::AmbiguousEcosystem("file.txt".into());
123        assert_eq!(
124            error.to_string(),
125            "ambiguous ecosystem detection for file: file.txt"
126        );
127    }
128}