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(transparent)]
86    #[diagnostic(code(soar_config::io))]
87    IoError(#[from] std::io::Error),
88
89    #[error(transparent)]
90    #[diagnostic(code(soar_config::utils))]
91    Utils(#[from] soar_utils::error::UtilsError),
92
93    #[error(transparent)]
94    #[diagnostic(code(soar_config::toml))]
95    Toml(#[from] toml_edit::TomlError),
96
97    #[error("Encountered unexpected TOML item: {0}")]
98    #[diagnostic(code(soar_config::unexpected_toml_item))]
99    UnexpectedTomlItem(String),
100
101    #[error("Failed to annotate first table in array: {0}")]
102    #[diagnostic(code(soar_config::annotate_first_table))]
103    AnnotateFirstTable(String),
104
105    #[error("{0}")]
106    #[diagnostic(code(soar_config::custom))]
107    Custom(String),
108}
109
110impl From<PathError> for ConfigError {
111    fn from(err: PathError) -> Self {
112        Self::Utils(UtilsError::Path(err))
113    }
114}
115
116impl From<soar_utils::error::BytesError> for ConfigError {
117    fn from(err: soar_utils::error::BytesError) -> Self {
118        Self::Utils(UtilsError::Bytes(err))
119    }
120}
121
122impl From<soar_utils::error::FileSystemError> for ConfigError {
123    fn from(err: soar_utils::error::FileSystemError) -> Self {
124        Self::Utils(UtilsError::FileSystem(err))
125    }
126}
127
128pub type Result<T> = std::result::Result<T, ConfigError>;
129
130#[cfg(test)]
131mod tests {
132    use super::*;
133
134    #[test]
135    fn test_error_display() {
136        let err = ConfigError::ConfigAlreadyExists;
137        assert_eq!(err.to_string(), "Configuration file already exists");
138
139        let err = ConfigError::InvalidProfile("test".to_string());
140        assert_eq!(err.to_string(), "Invalid profile: test");
141
142        let err = ConfigError::DuplicateRepositoryName("duplicate".to_string());
143        assert_eq!(err.to_string(), "Duplicate repository name: duplicate");
144    }
145
146    #[test]
147    fn test_error_from_conversions() {
148        let path_err = soar_utils::error::PathError::MissingEnvVar {
149            var: "SOAR_ROOT".to_string(),
150            input: "$SOR_ROOT/test".to_string(),
151        };
152        let config_err: ConfigError = path_err.into();
153        assert!(matches!(config_err, ConfigError::Utils(_)));
154    }
155}