apt_sources/
error.rs

1//! A module for handling errors in `apt-sources` crate of `deb822-rs` project.
2//! It intends to address error handling in meaningful manner, less vague than just passing
3//! `String` as error.
4
5/// Errors for APT sources parsing and conversion to `Repository`
6#[derive(Debug)]
7pub enum RepositoryError {
8    /// Invalid repository format
9    InvalidFormat,
10    /// Invalid repository URI
11    InvalidUri,
12    /// Missing repository URI - mandatory
13    MissingUri,
14    /// Unrecognized repository type
15    InvalidType,
16    /// The `Signed-By` field is incorrect
17    InvalidSignature,
18    /// Errors in lossy serializer or deserializer
19    Lossy(deb822_fast::Error),
20    /// I/O Error
21    Io(std::io::Error),
22}
23
24/// Errors that can occur when loading repositories from directories
25#[derive(Debug)]
26pub enum LoadError {
27    /// Failed to read a file
28    Io {
29        /// The path that failed to be read
30        path: std::path::PathBuf,
31        /// The underlying I/O error
32        error: std::io::Error,
33    },
34    /// Failed to parse a file
35    Parse {
36        /// The path that failed to be parsed
37        path: std::path::PathBuf,
38        /// The parsing error message
39        error: String,
40    },
41    /// Failed to read directory entries
42    DirectoryRead {
43        /// The directory path that failed to be read
44        path: std::path::PathBuf,
45        /// The underlying I/O error
46        error: std::io::Error,
47    },
48}
49
50impl From<std::io::Error> for RepositoryError {
51    fn from(e: std::io::Error) -> Self {
52        Self::Io(e)
53    }
54}
55
56impl std::fmt::Display for RepositoryError {
57    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
58        match self {
59            Self::InvalidFormat => write!(f, "Invalid repository format"),
60            Self::InvalidUri => write!(f, "Invalid repository URI"),
61            Self::MissingUri => write!(f, "Missing repository URI"),
62            Self::InvalidType => write!(f, "Invalid repository type"),
63            Self::InvalidSignature => write!(f, "The field `Signed-By` is incorrect"),
64            Self::Lossy(e) => write!(f, "Lossy parser error: {}", e),
65            Self::Io(e) => write!(f, "IO error: {}", e),
66        }
67    }
68}
69
70impl std::fmt::Display for LoadError {
71    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
72        match self {
73            Self::Io { path, error } => write!(f, "Failed to read {}: {}", path.display(), error),
74            Self::Parse { path, error } => {
75                write!(f, "Failed to parse {}: {}", path.display(), error)
76            }
77            Self::DirectoryRead { path, error } => {
78                write!(f, "Failed to read directory {}: {}", path.display(), error)
79            }
80        }
81    }
82}
83
84impl std::error::Error for RepositoryError {}
85impl std::error::Error for LoadError {}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90
91    #[test]
92    fn test_repository_error_display() {
93        // Test each error variant
94        assert_eq!(
95            RepositoryError::InvalidFormat.to_string(),
96            "Invalid repository format"
97        );
98        assert_eq!(
99            RepositoryError::InvalidUri.to_string(),
100            "Invalid repository URI"
101        );
102        assert_eq!(
103            RepositoryError::MissingUri.to_string(),
104            "Missing repository URI"
105        );
106        assert_eq!(
107            RepositoryError::InvalidType.to_string(),
108            "Invalid repository type"
109        );
110        assert_eq!(
111            RepositoryError::InvalidSignature.to_string(),
112            "The field `Signed-By` is incorrect"
113        );
114
115        // Test lossy error
116        let lossy_err = deb822_fast::Error::UnexpectedEof;
117        let repo_err = RepositoryError::Lossy(lossy_err);
118        assert!(repo_err.to_string().contains("Lossy parser error:"));
119
120        // Test IO error
121        let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
122        let repo_err = RepositoryError::from(io_err);
123        assert!(repo_err.to_string().contains("IO error:"));
124    }
125
126    #[test]
127    fn test_load_error_display() {
128        use std::path::PathBuf;
129
130        // Test IO error
131        let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
132        let load_err = LoadError::Io {
133            path: PathBuf::from("/test/path"),
134            error: io_err,
135        };
136        assert!(load_err.to_string().contains("Failed to read /test/path"));
137        assert!(load_err.to_string().contains("file not found"));
138
139        // Test Parse error
140        let parse_err = LoadError::Parse {
141            path: PathBuf::from("/test/file.list"),
142            error: "Invalid format".to_string(),
143        };
144        assert!(parse_err
145            .to_string()
146            .contains("Failed to parse /test/file.list"));
147        assert!(parse_err.to_string().contains("Invalid format"));
148
149        // Test DirectoryRead error
150        let dir_err = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "access denied");
151        let load_err = LoadError::DirectoryRead {
152            path: PathBuf::from("/test/dir"),
153            error: dir_err,
154        };
155        assert!(load_err
156            .to_string()
157            .contains("Failed to read directory /test/dir"));
158        assert!(load_err.to_string().contains("access denied"));
159    }
160}