referencing/
error.rs

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