1use std::path::PathBuf;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum StyleValidationError {
10 UnresolvedAlias { from: String, to: String },
12 CycleDetected { path: Vec<String> },
14}
15
16impl std::fmt::Display for StyleValidationError {
17 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18 match self {
19 StyleValidationError::UnresolvedAlias { from, to } => {
20 write!(f, "style '{}' aliases non-existent style '{}'", from, to)
21 }
22 StyleValidationError::CycleDetected { path } => {
23 write!(f, "cycle detected in style aliases: {}", path.join(" -> "))
24 }
25 }
26 }
27}
28
29impl std::error::Error for StyleValidationError {}
30
31#[derive(Debug, Clone, PartialEq, Eq)]
33pub enum StylesheetError {
34 Parse {
36 path: Option<PathBuf>,
38 message: String,
40 },
41
42 InvalidColor {
44 style: String,
46 value: String,
48 path: Option<PathBuf>,
50 },
51
52 UnknownAttribute {
54 style: String,
56 attribute: String,
58 path: Option<PathBuf>,
60 },
61
62 InvalidShorthand {
64 style: String,
66 value: String,
68 path: Option<PathBuf>,
70 },
71
72 AliasError {
74 source: StyleValidationError,
76 },
77
78 InvalidDefinition {
80 style: String,
82 message: String,
84 path: Option<PathBuf>,
86 },
87
88 Load {
90 message: String,
92 },
93}
94
95impl std::fmt::Display for StylesheetError {
96 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97 match self {
98 StylesheetError::Parse { path, message } => {
99 if let Some(p) = path {
100 write!(f, "Failed to parse stylesheet {}: {}", p.display(), message)
101 } else {
102 write!(f, "Failed to parse stylesheet: {}", message)
103 }
104 }
105 StylesheetError::InvalidColor { style, value, path } => {
106 let location = path
107 .as_ref()
108 .map(|p| format!(" in {}", p.display()))
109 .unwrap_or_default();
110 write!(
111 f,
112 "Invalid color '{}' for style '{}'{}",
113 value, style, location
114 )
115 }
116 StylesheetError::UnknownAttribute {
117 style,
118 attribute,
119 path,
120 } => {
121 let location = path
122 .as_ref()
123 .map(|p| format!(" in {}", p.display()))
124 .unwrap_or_default();
125 write!(
126 f,
127 "Unknown attribute '{}' in style '{}'{}",
128 attribute, style, location
129 )
130 }
131 StylesheetError::InvalidShorthand { style, value, path } => {
132 let location = path
133 .as_ref()
134 .map(|p| format!(" in {}", p.display()))
135 .unwrap_or_default();
136 write!(
137 f,
138 "Invalid shorthand '{}' for style '{}'{}",
139 value, style, location
140 )
141 }
142 StylesheetError::AliasError { source } => {
143 write!(f, "Style alias error: {}", source)
144 }
145 StylesheetError::InvalidDefinition {
146 style,
147 message,
148 path,
149 } => {
150 let location = path
151 .as_ref()
152 .map(|p| format!(" in {}", p.display()))
153 .unwrap_or_default();
154 write!(
155 f,
156 "Invalid definition for style '{}'{}: {}",
157 style, location, message
158 )
159 }
160 StylesheetError::Load { message } => {
161 write!(f, "Failed to load stylesheet: {}", message)
162 }
163 }
164 }
165}
166
167impl std::error::Error for StylesheetError {
168 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
169 match self {
170 StylesheetError::AliasError { source } => Some(source),
171 _ => None,
172 }
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 use super::*;
179
180 #[test]
181 fn test_unresolved_alias_error_display() {
182 let err = StyleValidationError::UnresolvedAlias {
183 from: "orphan".to_string(),
184 to: "missing".to_string(),
185 };
186 let msg = err.to_string();
187 assert!(msg.contains("orphan"));
188 assert!(msg.contains("missing"));
189 }
190
191 #[test]
192 fn test_cycle_detected_error_display() {
193 let err = StyleValidationError::CycleDetected {
194 path: vec!["a".to_string(), "b".to_string(), "a".to_string()],
195 };
196 let msg = err.to_string();
197 assert!(msg.contains("cycle"));
198 assert!(msg.contains("a -> b -> a"));
199 }
200}