graphrecords_query/error/
mod.rs1pub mod aggregation;
2pub mod argument;
3pub mod arithmetic;
4pub mod comparison;
5pub mod conversion;
6#[cfg(feature = "dynamic")]
7pub mod dispatch;
8pub mod execution;
9pub mod grouping;
10pub mod groups;
11pub mod index;
12pub mod numeric;
13pub mod ordering;
14pub mod string;
15pub mod structure;
16
17use crate::{IndexDomain, OwnedIndex};
18use graphrecords_core::errors::GraphRecordError;
19use std::{
20 any::{Any, TypeId},
21 error::Error,
22 fmt::{self, Display, Formatter},
23 hash::{Hash, Hasher},
24 sync::Arc,
25};
26
27pub type QueryResult<T> = Result<T, Box<Failure>>;
28
29pub trait Diagnostic: Error + Send + Sync + 'static {
30 fn name() -> &'static str
31 where
32 Self: Sized;
33
34 fn help(&self) -> Option<String> {
35 None
36 }
37}
38
39#[derive(Clone, Copy, Debug)]
40pub struct FailureKind {
41 identifier: TypeId,
42 name: &'static str,
43}
44
45impl FailureKind {
46 #[must_use]
47 pub fn of<D: Diagnostic>() -> Self {
48 Self {
49 identifier: TypeId::of::<D>(),
50 name: D::name(),
51 }
52 }
53
54 #[must_use]
55 pub fn is<D: Diagnostic>(&self) -> bool {
56 self.identifier == TypeId::of::<D>()
57 }
58
59 #[must_use]
60 pub const fn name(&self) -> &'static str {
61 self.name
62 }
63}
64
65impl Display for FailureKind {
66 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
67 formatter.write_str(self.name)
68 }
69}
70
71impl PartialEq for FailureKind {
72 fn eq(&self, other: &Self) -> bool {
73 self.identifier == other.identifier
74 }
75}
76
77impl Eq for FailureKind {}
78
79impl Hash for FailureKind {
80 fn hash<H: Hasher>(&self, state: &mut H) {
81 self.identifier.hash(state);
82 }
83}
84
85pub trait ErrorGroup: 'static {
86 fn name() -> &'static str
87 where
88 Self: Sized;
89
90 fn contains(kind: &FailureKind) -> bool;
91}
92
93#[derive(Clone, Debug)]
94pub struct Failure {
95 operation: &'static str,
96 element: Option<Arc<dyn OwnedIndex>>,
97 kind: FailureKind,
98 cause: Arc<dyn Diagnostic>,
99}
100
101impl Failure {
102 pub fn new<D: Diagnostic>(operation: &'static str, cause: D) -> Box<Self> {
103 Box::new(Self {
104 operation,
105 element: None,
106 kind: FailureKind::of::<D>(),
107 cause: Arc::new(cause),
108 })
109 }
110
111 pub fn new_at<I: IndexDomain, D: Diagnostic>(
112 operation: &'static str,
113 cause: D,
114 index: &I::Index<'_>,
115 ) -> Box<Self> {
116 Box::new(Self {
117 operation,
118 element: Some(Arc::new(I::to_owned(index))),
119 kind: FailureKind::of::<D>(),
120 cause: Arc::new(cause),
121 })
122 }
123
124 #[must_use]
125 pub fn at<I: IndexDomain>(mut self: Box<Self>, index: &I::Index<'_>) -> Box<Self> {
126 self.element = Some(Arc::new(I::to_owned(index)));
127 self
128 }
129
130 #[must_use]
131 pub const fn operation(&self) -> &'static str {
132 self.operation
133 }
134
135 #[must_use]
136 pub fn element(&self) -> Option<&dyn OwnedIndex> {
137 self.element.as_deref()
138 }
139
140 #[must_use]
141 pub fn downcast_element<T: OwnedIndex>(&self) -> Option<&T> {
142 let element: &dyn Any = self.element.as_deref()?;
143
144 element.downcast_ref()
145 }
146
147 #[must_use]
148 pub fn cause(&self) -> &dyn Diagnostic {
149 self.cause.as_ref()
150 }
151
152 #[must_use]
153 pub const fn kind(&self) -> FailureKind {
154 self.kind
155 }
156
157 #[must_use]
158 pub fn is_kind<D: Diagnostic>(&self) -> bool {
159 self.kind.is::<D>()
160 }
161
162 #[must_use]
163 pub fn help(&self) -> Option<String> {
164 self.cause.help()
165 }
166
167 #[must_use]
168 pub fn downcast_cause<T: Error + 'static>(&self) -> Option<&T> {
169 let mut current: &(dyn Error + 'static) = self.cause.as_ref();
170
171 loop {
172 if let Some(cause) = current.downcast_ref() {
173 return Some(cause);
174 }
175
176 current = current.source()?;
177 }
178 }
179
180 #[must_use]
181 pub fn has_cause<T: Error + 'static>(&self) -> bool {
182 self.downcast_cause::<T>().is_some()
183 }
184}
185
186impl Display for Failure {
187 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
188 match self.element() {
189 Some(element) => write!(
190 formatter,
191 "operation `{}` failed at element `{element}`: {}",
192 self.operation, self.cause,
193 )?,
194 None => write!(
195 formatter,
196 "operation `{}` failed: {}",
197 self.operation, self.cause,
198 )?,
199 }
200
201 if let Some(help) = self.help() {
202 write!(formatter, "\nhelp: {help}")?;
203 }
204
205 Ok(())
206 }
207}
208
209impl Error for Failure {}
210
211#[derive(Debug)]
212pub struct External<E: Error + Send + Sync + 'static>(E);
213
214impl<E: Error + Send + Sync + 'static> External<E> {
215 #[must_use]
216 pub const fn new(error: E) -> Self {
217 Self(error)
218 }
219
220 #[must_use]
221 pub const fn error(&self) -> &E {
222 &self.0
223 }
224}
225
226impl<E: Error + Send + Sync + 'static> Display for External<E> {
227 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
228 Display::fmt(&self.0, formatter)
229 }
230}
231
232impl<E: Error + Send + Sync + 'static> Error for External<E> {
233 fn source(&self) -> Option<&(dyn Error + 'static)> {
234 Some(&self.0)
235 }
236}
237
238impl<E: Error + Send + Sync + 'static> Diagnostic for External<E> {
239 fn name() -> &'static str {
240 "External"
241 }
242}
243
244impl Diagnostic for GraphRecordError {
245 fn name() -> &'static str {
246 "GraphRecordError"
247 }
248
249 fn help(&self) -> Option<String> {
250 match self {
251 Self::IncompatibleValueOperands { .. } | Self::IncompatibleAttributeOperands { .. } => {
252 Some(
253 "narrow the values down first using is_string(), is_int(), is_float(), is_bool(), is_datetime() or is_duration()"
254 .to_string(),
255 )
256 }
257 Self::GroupNotFound { .. } => {
258 Some("add the group first or check `groups()` before querying".to_string())
259 }
260 _ => None,
261 }
262 }
263}