1use std::fmt;
4use thiserror::Error;
5
6#[derive(Error, Debug)]
8pub enum DotlyteError {
9 #[error("Required config key '{key}' is missing.{}", format_sources(.sources_checked))]
11 MissingKey {
12 key: String,
13 sources_checked: Vec<String>,
14 },
15
16 #[error("Parse error in {file}: {message}")]
18 ParseError { file: String, message: String },
19
20 #[error("File error for '{file}': {message}")]
22 FileError { file: String, message: String },
23
24 #[error("Schema validation failed with {} violation(s): {}", .violations.len(), format_violations(.violations))]
26 ValidationError { violations: Vec<SchemaViolation> },
27
28 #[error("Interpolation error for '${{{{{}}}}}': {message}", .variable)]
30 InterpolationError { variable: String, message: String },
31
32 #[error("Decryption error: {message}")]
34 DecryptionError { message: String },
35
36 #[error("I/O error: {0}")]
38 Io(#[from] std::io::Error),
39}
40
41#[derive(Debug, Clone)]
43pub struct SchemaViolation {
44 pub key: String,
45 pub message: String,
46 pub rule: String,
47}
48
49impl fmt::Display for SchemaViolation {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 write!(f, "{}: {} ({})", self.key, self.message, self.rule)
52 }
53}
54
55pub type Result<T> = std::result::Result<T, DotlyteError>;
57
58fn format_sources(sources: &[String]) -> String {
59 if sources.is_empty() {
60 String::new()
61 } else {
62 format!(" Sources checked: {}", sources.join(", "))
63 }
64}
65
66fn format_violations(violations: &[SchemaViolation]) -> String {
67 violations
68 .iter()
69 .map(|v| v.to_string())
70 .collect::<Vec<_>>()
71 .join("; ")
72}