Skip to main content

immutable_json/
error.rs

1#[cfg(feature = "patch")]
2use crate::api::Value;
3#[cfg(feature = "patch")]
4use crate::array::Array;
5#[cfg(feature = "jaq")]
6use jaq_core::compile::Errors;
7#[cfg(feature = "jaq")]
8use jaq_core::load::File;
9use std::fmt;
10use std::fmt::{Display, Formatter};
11#[cfg(feature = "patch")]
12use std::path::PathBuf;
13
14#[derive(Debug)]
15pub enum Error {
16    /// Cannot convert from another JSON representation to an immutable JSON value.
17    ConvertFrom,
18    /// Cannot convert from an immutable JSON value to another JSON representation.
19    ConvertTo,
20    #[cfg(feature = "jaq")]
21    /// A jaq compilation error. It requires the `jaq` crate feature.
22    JaqCompile(String),
23    #[cfg(feature = "jaq")]
24    /// An exception while running a jaq filter. It requires the `jaq` crate feature.
25    JaqException(String),
26    #[cfg(feature = "jaq")]
27    /// A jaq transformation didn't produce any result. It requires the `jaq` crate feature.
28    JaqNoResult,
29    /// Wrong array index.
30    JsonIndex(JsonIndexError),
31    #[cfg(feature = "patch")]
32    /// Invalid JSON patch.
33    JsonPatch(JsonPatchError),
34    /// Invalid JSON pointer.
35    JsonPointer(String),
36    /// A serde_json error.
37    SerdeJson(serde_json::Error),
38}
39
40impl Display for Error {
41    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
42        match self {
43            Error::ConvertFrom => write!(f, "cannot convert from serde_json, jaq_json or BSON"),
44            Error::ConvertTo => write!(f, "cannot convert to serde_json, jaq_json or BSON"),
45            #[cfg(feature = "jaq")]
46            Error::JaqCompile(s) => write!(f, "jaq compilation error: {}", s),
47            #[cfg(feature = "jaq")]
48            Error::JaqException(s) => write!(f, "jaq exception: {}", s),
49            #[cfg(feature = "jaq")]
50            Error::JaqNoResult => write!(f, "the jq expression did not yield a result"),
51            Error::JsonIndex(i) => write!(f, "{}", i),
52            #[cfg(feature = "patch")]
53            Error::JsonPatch(p) => write!(f, "{}", p),
54            Error::JsonPointer(p) => write!(f, "malformed JSON pointer {}", p),
55            Error::SerdeJson(s) => write!(f, "{}", s),
56        }
57    }
58}
59
60impl std::error::Error for Error {}
61
62impl From<JsonIndexError> for Error {
63    fn from(value: JsonIndexError) -> Self {
64        Error::JsonIndex(value)
65    }
66}
67
68#[cfg(feature = "patch")]
69impl From<JsonPatchError> for Error {
70    fn from(value: JsonPatchError) -> Self {
71        Error::JsonPatch(value)
72    }
73}
74
75impl From<serde_json::Error> for Error {
76    fn from(value: serde_json::Error) -> Self {
77        Error::SerdeJson(value)
78    }
79}
80
81#[cfg(feature = "jaq")]
82impl From<Vec<(File<&str, PathBuf>, jaq_core::load::Error<&str>)>> for Error {
83    fn from(errors: Vec<(File<&str, PathBuf>, jaq_core::load::Error<&str>)>) -> Self {
84        Error::JaqCompile(
85            errors
86                .iter()
87                .map(|(file, error)| {
88                    pathbuf_to_string(file.path.clone())
89                        + ": code: "
90                        + file.code
91                        + ": error: "
92                        + &jaq_load_errors(error)
93                })
94                .fold(String::new(), |s, el| s + "\n" + &el),
95        )
96    }
97}
98
99#[cfg(feature = "jaq")]
100impl<'a> From<Errors<&'a str, PathBuf>> for Error {
101    fn from(errors: Errors<&'a str, PathBuf>) -> Self {
102        Error::JaqCompile(
103            errors
104                .iter()
105                .map(|(file, error)| {
106                    pathbuf_to_string(file.path.clone())
107                        + ": code: "
108                        + file.code
109                        + ":error: "
110                        + &jaq_compile_errors(error)
111                })
112                .fold(String::new(), |s, el| s + "\n" + &el),
113        )
114    }
115}
116
117/// Denotes a wrong JSON array index.
118#[derive(Debug, PartialEq)]
119pub struct JsonIndexError {
120    /// The index.
121    pub index: usize,
122    /// The length of the array.
123    pub len: usize,
124}
125
126impl Display for JsonIndexError {
127    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
128        write!(
129            f,
130            "the index {} is higher than {}",
131            self.index,
132            self.len - 1
133        )
134    }
135}
136
137#[derive(Debug)]
138#[cfg(feature = "patch")]
139pub struct JsonPatchError {
140    pub error: String,
141    pub patch: Box<Array>,
142    pub source: Box<Value>,
143}
144
145#[cfg(feature = "patch")]
146impl Display for JsonPatchError {
147    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
148        write!(
149            f,
150            "cannot apply path '{}' to source '{}' because of '{}",
151            self.patch, self.source, self.error
152        )
153    }
154}
155
156#[cfg(feature = "jaq")]
157fn jaq_compile_errors(errors: &Vec<jaq_core::compile::Error<&str>>) -> String {
158    errors
159        .iter()
160        .map(|(name, undefined)| undefined.as_str().to_string() + " " + name + " is undefined")
161        .fold(String::new(), |s, el| s + "\n" + &el)
162}
163
164#[cfg(feature = "jaq")]
165fn jaq_load_errors(error: &jaq_core::load::Error<&str>) -> String {
166    match error {
167        jaq_core::load::Error::Io(v) => v
168            .iter()
169            .map(|(p, e)| p.to_string() + ": " + e)
170            .fold(String::new(), |s, el| s + "\n" + &el),
171        jaq_core::load::Error::Lex(v) => jaq_read_error(v.iter().map(|(e, g)| (e.as_str(), *g))),
172        jaq_core::load::Error::Parse(v) => jaq_read_error(v.iter().map(|(e, g)| (e.as_str(), *g))),
173    }
174}
175
176#[cfg(feature = "jaq")]
177fn jaq_read_error<'a>(errors: impl Iterator<Item = (&'a str, &'a str)>) -> String {
178    errors
179        .map(|(e, g)| "expected ".to_string() + e + ", got " + g)
180        .fold(String::new(), |s, el| s + "\n" + &el)
181}
182
183#[cfg(feature = "jaq")]
184fn pathbuf_to_string(path_buf: PathBuf) -> String {
185    path_buf.into_os_string().to_str().unwrap_or("").to_string()
186}