referencing/
error.rs

1use core::fmt;
2use std::{num::ParseIntError, str::Utf8Error};
3
4use fluent_uri::{resolve::ResolveError, ParseError, Uri};
5
6/// Errors that can occur during reference resolution and resource handling.
7#[derive(Debug)]
8pub enum Error {
9    /// A resource is not present in a registry and retrieving it failed.
10    Unretrievable {
11        uri: String,
12        source: Box<dyn std::error::Error + Send + Sync>,
13    },
14    /// A JSON Pointer leads to a part of a document that does not exist.
15    PointerToNowhere { pointer: String },
16    /// JSON Pointer contains invalid percent-encoded data.
17    InvalidPercentEncoding { pointer: String, source: Utf8Error },
18    /// Failed to parse array index in JSON Pointer.
19    InvalidArrayIndex {
20        pointer: String,
21        index: String,
22        source: ParseIntError,
23    },
24    /// An anchor does not exist within a particular resource.
25    NoSuchAnchor { anchor: String },
26    /// An anchor which could never exist in a resource was dereferenced.
27    InvalidAnchor { anchor: String },
28    /// An error occurred while parsing or manipulating a URI.
29    InvalidUri(UriError),
30    /// An unknown JSON Schema specification was encountered.
31    UnknownSpecification { specification: String },
32    /// A circular reference was detected in a meta-schema chain.
33    CircularMetaschema { uri: String },
34}
35
36impl Error {
37    pub(crate) fn pointer_to_nowhere(pointer: impl Into<String>) -> Error {
38        Error::PointerToNowhere {
39            pointer: pointer.into(),
40        }
41    }
42    pub(crate) fn invalid_percent_encoding(pointer: impl Into<String>, source: Utf8Error) -> Error {
43        Error::InvalidPercentEncoding {
44            pointer: pointer.into(),
45            source,
46        }
47    }
48    pub(crate) fn invalid_array_index(
49        pointer: impl Into<String>,
50        index: impl Into<String>,
51        source: ParseIntError,
52    ) -> Error {
53        Error::InvalidArrayIndex {
54            pointer: pointer.into(),
55            index: index.into(),
56            source,
57        }
58    }
59    pub(crate) fn invalid_anchor(anchor: impl Into<String>) -> Error {
60        Error::InvalidAnchor {
61            anchor: anchor.into(),
62        }
63    }
64    pub(crate) fn no_such_anchor(anchor: impl Into<String>) -> Error {
65        Error::NoSuchAnchor {
66            anchor: anchor.into(),
67        }
68    }
69
70    pub fn unknown_specification(specification: impl Into<String>) -> Error {
71        Error::UnknownSpecification {
72            specification: specification.into(),
73        }
74    }
75
76    pub fn circular_metaschema(uri: impl Into<String>) -> Error {
77        Error::CircularMetaschema { uri: uri.into() }
78    }
79
80    pub fn unretrievable(
81        uri: impl Into<String>,
82        source: Box<dyn std::error::Error + Send + Sync>,
83    ) -> Error {
84        Error::Unretrievable {
85            uri: uri.into(),
86            source,
87        }
88    }
89
90    pub(crate) fn uri_parsing_error(uri: impl Into<String>, error: ParseError) -> Error {
91        Error::InvalidUri(UriError::Parse {
92            uri: uri.into(),
93            is_reference: false,
94            error,
95        })
96    }
97
98    pub(crate) fn uri_reference_parsing_error(uri: impl Into<String>, error: ParseError) -> Error {
99        Error::InvalidUri(UriError::Parse {
100            uri: uri.into(),
101            is_reference: true,
102            error,
103        })
104    }
105
106    pub(crate) fn uri_resolving_error(
107        uri: impl Into<String>,
108        base: Uri<&str>,
109        error: ResolveError,
110    ) -> Error {
111        Error::InvalidUri(UriError::Resolve {
112            uri: uri.into(),
113            base: base.to_owned(),
114            error,
115        })
116    }
117}
118
119impl fmt::Display for Error {
120    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121        match self {
122            Error::Unretrievable { uri, source } => {
123                f.write_fmt(format_args!("Resource '{uri}' is not present in a registry and retrieving it failed: {source}"))
124            },
125            Error::PointerToNowhere { pointer } => {
126                f.write_fmt(format_args!("Pointer '{pointer}' does not exist"))
127            }
128            Error::InvalidPercentEncoding { pointer, .. } => {
129                f.write_fmt(format_args!("Invalid percent encoding in pointer '{pointer}': the decoded bytes do not represent valid UTF-8"))
130            }
131            Error::InvalidArrayIndex { pointer, index, .. } => {
132                f.write_fmt(format_args!("Failed to parse array index '{index}' in pointer '{pointer}'"))
133            }
134            Error::NoSuchAnchor { anchor } => {
135                f.write_fmt(format_args!("Anchor '{anchor}' does not exist"))
136            }
137            Error::InvalidAnchor { anchor } => {
138                f.write_fmt(format_args!("Anchor '{anchor}' is invalid"))
139            }
140            Error::InvalidUri(error) => error.fmt(f),
141            Error::UnknownSpecification { specification } => {
142                write!(f, "Unknown meta-schema: '{specification}'. Custom meta-schemas must be registered in the registry before use")
143            }
144            Error::CircularMetaschema { uri } => {
145                write!(f, "Circular meta-schema reference detected at '{uri}'")
146            }
147        }
148    }
149}
150
151impl std::error::Error for Error {
152    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
153        match self {
154            Error::Unretrievable { source, .. } => Some(&**source),
155            Error::InvalidUri(error) => Some(error),
156            Error::InvalidPercentEncoding { source, .. } => Some(source),
157            Error::InvalidArrayIndex { source, .. } => Some(source),
158            _ => None,
159        }
160    }
161}
162
163/// Errors that can occur during URI handling.
164#[derive(Debug)]
165pub enum UriError {
166    Parse {
167        uri: String,
168        is_reference: bool,
169        error: ParseError,
170    },
171    Resolve {
172        uri: String,
173        base: Uri<String>,
174        error: ResolveError,
175    },
176}
177
178impl fmt::Display for UriError {
179    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180        match self {
181            UriError::Parse {
182                uri,
183                is_reference,
184                error,
185            } => {
186                if *is_reference {
187                    f.write_fmt(format_args!("Invalid URI reference '{uri}': {error}"))
188                } else {
189                    f.write_fmt(format_args!("Invalid URI '{uri}': {error}"))
190                }
191            }
192            UriError::Resolve { uri, base, error } => f.write_fmt(format_args!(
193                "Failed to resolve '{uri}' against '{base}': {error}"
194            )),
195        }
196    }
197}
198
199impl std::error::Error for UriError {
200    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
201        match self {
202            UriError::Parse { error, .. } => Some(error),
203            UriError::Resolve { error, .. } => Some(error),
204        }
205    }
206}