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}