jsonschema_annotator/
error.rs

1use std::borrow::Cow;
2
3/// A generic error type with context chaining and hidden source errors.
4#[derive(Debug)]
5pub struct Error<K> {
6    pub kind: K,
7    pub(crate) context: Vec<Cow<'static, str>>,
8    pub(crate) source: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
9}
10
11impl<K> Error<K> {
12    pub fn new(kind: K) -> Self {
13        Self {
14            kind,
15            context: Vec::new(),
16            source: None,
17        }
18    }
19
20    pub fn map_kind<NK, F>(self, mapper: F) -> Error<NK>
21    where
22        F: Fn(K) -> NK,
23    {
24        Error {
25            kind: mapper(self.kind),
26            context: self.context,
27            source: self.source,
28        }
29    }
30
31    pub fn add_context(mut self, context: impl Into<Cow<'static, str>>) -> Self {
32        self.context.push(context.into());
33        self
34    }
35
36    pub fn with_source(mut self, source: impl std::error::Error + Send + Sync + 'static) -> Self {
37        self.source = Some(Box::new(source));
38        self
39    }
40
41    pub fn with_boxed_source(
42        mut self,
43        source: Box<dyn std::error::Error + Send + Sync + 'static>,
44    ) -> Self {
45        self.source = Some(source);
46        self
47    }
48}
49
50impl<K> From<K> for Error<K> {
51    fn from(value: K) -> Self {
52        Error::new(value)
53    }
54}
55
56pub trait ResultExt<T, K> {
57    fn add_context(self, context: impl Into<Cow<'static, str>>) -> Result<T, Error<K>>;
58    fn add_context_fn<C: Into<Cow<'static, str>>, F: FnOnce() -> C>(
59        self,
60        context: F,
61    ) -> Result<T, Error<K>>;
62}
63
64impl<T, K> ResultExt<T, K> for Result<T, Error<K>> {
65    fn add_context(self, context: impl Into<Cow<'static, str>>) -> Result<T, Error<K>> {
66        match self {
67            Ok(value) => Ok(value),
68            Err(error) => Err(error.add_context(context)),
69        }
70    }
71
72    fn add_context_fn<C: Into<Cow<'static, str>>, F: FnOnce() -> C>(
73        self,
74        context: F,
75    ) -> Result<T, Error<K>> {
76        match self {
77            Ok(value) => Ok(value),
78            Err(error) => Err(error.add_context(context().into())),
79        }
80    }
81}
82
83impl<K> std::fmt::Display for Error<K>
84where
85    K: std::fmt::Display,
86{
87    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88        self.kind.fmt(f)?;
89
90        if !self.context.is_empty() {
91            write!(f, " context: [")?;
92            for (i, context) in self.context.iter().rev().enumerate() {
93                write!(f, "{}", context)?;
94                if i < self.context.len() - 1 {
95                    write!(f, ", ")?;
96                }
97            }
98            write!(f, "]")?;
99        }
100
101        Ok(())
102    }
103}
104
105impl<K> std::error::Error for Error<K>
106where
107    K: std::fmt::Display + std::fmt::Debug,
108{
109    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
110        self.source.as_ref().map(|s| s.as_ref() as _)
111    }
112}
113
114// Error kinds for schema operations
115#[derive(Debug)]
116pub enum SchemaErrorKind {
117    Io,
118    ValueParse,
119    InvalidSchema,
120    RefResolution,
121}
122
123impl std::fmt::Display for SchemaErrorKind {
124    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
125        match self {
126            SchemaErrorKind::Io => write!(f, "I/O error"),
127            SchemaErrorKind::ValueParse => write!(f, "failed to parse value"),
128            SchemaErrorKind::InvalidSchema => write!(f, "invalid schema"),
129            SchemaErrorKind::RefResolution => write!(f, "failed to resolve $ref"),
130        }
131    }
132}
133
134// Error kinds for annotator operations
135#[derive(Debug)]
136pub enum AnnotatorErrorKind {
137    Parse,
138    Io,
139}
140
141impl std::fmt::Display for AnnotatorErrorKind {
142    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
143        match self {
144            AnnotatorErrorKind::Parse => write!(f, "failed to parse target document"),
145            AnnotatorErrorKind::Io => write!(f, "I/O error"),
146        }
147    }
148}
149
150pub type SchemaError = Error<SchemaErrorKind>;
151pub type AnnotatorError = Error<AnnotatorErrorKind>;