Skip to main content

soar_config/
error.rs

1use miette::Diagnostic;
2use soar_utils::error::{PathError, UtilsError};
3use thiserror::Error;
4
5#[derive(Error, Diagnostic, Debug)]
6pub enum ConfigError {
7    #[error(transparent)]
8    #[diagnostic(
9        code(soar_config::toml_serialize),
10        help("Check your configuration structure for invalid values")
11    )]
12    TomlSerError(#[from] toml::ser::Error),
13
14    #[error(transparent)]
15    #[diagnostic(
16        code(soar_config::toml_deserialize),
17        help("Check your config.toml syntax and structure")
18    )]
19    TomlDeError(#[from] toml::de::Error),
20
21    #[error("Configuration file already exists")]
22    #[diagnostic(
23        code(soar_config::already_exists),
24        help("Remove the existing config file or use a different location")
25    )]
26    ConfigAlreadyExists,
27
28    #[error("Packages configuration file not found: {0}")]
29    #[diagnostic(
30        code(soar_config::packages_not_found),
31        help("Create a packages.toml file or run `soar defpackages` to generate one")
32    )]
33    PackagesConfigNotFound(String),
34
35    #[error("Packages configuration file already exists")]
36    #[diagnostic(
37        code(soar_config::packages_already_exists),
38        help("Remove the existing packages.toml file or use a different location")
39    )]
40    PackagesConfigAlreadyExists,
41
42    #[error("Invalid profile: {0}")]
43    #[diagnostic(
44        code(soar_config::invalid_profile),
45        help("Check available profiles in your config file")
46    )]
47    InvalidProfile(String),
48
49    #[error("Missing default profile: {0}")]
50    #[diagnostic(
51        code(soar_config::missing_default_profile),
52        help("Ensure the default_profile field references an existing profile")
53    )]
54    MissingDefaultProfile(String),
55
56    #[error("Missing profile: {0}")]
57    #[diagnostic(
58        code(soar_config::missing_profile),
59        help("Add the profile to your configuration or use an existing one")
60    )]
61    MissingProfile(String),
62
63    #[error("Invalid repository name: {0}")]
64    #[diagnostic(code(soar_config::invalid_repository))]
65    InvalidRepository(String),
66
67    #[error("Invalid repository URL: {0}")]
68    #[diagnostic(code(soar_config::invalid_repository_url))]
69    InvalidRepositoryUrl(String),
70
71    #[error("Reserved repository name 'local' cannot be used")]
72    #[diagnostic(
73        code(soar_config::reserved_repo_name),
74        help("Choose a different name for your repository")
75    )]
76    ReservedRepositoryName,
77
78    #[error("Duplicate repository name: {0}")]
79    #[diagnostic(
80        code(soar_config::duplicate_repo),
81        help("Each repository must have a unique name")
82    )]
83    DuplicateRepositoryName(String),
84
85    #[error("Repository '{0}' has signature verification enabled but no pubkey configured")]
86    #[diagnostic(
87        code(soar_config::missing_pubkey),
88        help("Provide a pubkey for the repository or disable signature_verification")
89    )]
90    MissingPubkey(String),
91
92    #[error(transparent)]
93    #[diagnostic(code(soar_config::io))]
94    IoError(#[from] std::io::Error),
95
96    #[error(transparent)]
97    #[diagnostic(code(soar_config::utils))]
98    Utils(#[from] soar_utils::error::UtilsError),
99
100    #[error(transparent)]
101    #[diagnostic(code(soar_config::toml))]
102    Toml(#[from] toml_edit::TomlError),
103
104    #[error("Encountered unexpected TOML item: {0}")]
105    #[diagnostic(code(soar_config::unexpected_toml_item))]
106    UnexpectedTomlItem(String),
107
108    #[error("Failed to annotate first table in array: {0}")]
109    #[diagnostic(code(soar_config::annotate_first_table))]
110    AnnotateFirstTable(String),
111
112    #[error("{0}")]
113    #[diagnostic(code(soar_config::custom))]
114    Custom(String),
115}
116
117impl From<PathError> for ConfigError {
118    fn from(err: PathError) -> Self {
119        Self::Utils(UtilsError::Path(err))
120    }
121}
122
123impl From<soar_utils::error::BytesError> for ConfigError {
124    fn from(err: soar_utils::error::BytesError) -> Self {
125        Self::Utils(UtilsError::Bytes(err))
126    }
127}
128
129impl From<soar_utils::error::FileSystemError> for ConfigError {
130    fn from(err: soar_utils::error::FileSystemError) -> Self {
131        Self::Utils(UtilsError::FileSystem(err))
132    }
133}
134
135pub type Result<T> = std::result::Result<T, ConfigError>;
136
137#[cfg(test)]
138mod tests {
139    use super::*;
140
141    #[test]
142    fn test_error_display() {
143        let err = ConfigError::ConfigAlreadyExists;
144        assert_eq!(err.to_string(), "Configuration file already exists");
145
146        let err = ConfigError::InvalidProfile("test".to_string());
147        assert_eq!(err.to_string(), "Invalid profile: test");
148
149        let err = ConfigError::DuplicateRepositoryName("duplicate".to_string());
150        assert_eq!(err.to_string(), "Duplicate repository name: duplicate");
151    }
152
153    #[test]
154    fn test_error_from_conversions() {
155        let path_err = soar_utils::error::PathError::MissingEnvVar {
156            var: "SOAR_ROOT".to_string(),
157            input: "$SOR_ROOT/test".to_string(),
158        };
159        let config_err: ConfigError = path_err.into();
160        assert!(matches!(config_err, ConfigError::Utils(_)));
161    }
162}