1use std::convert::Infallible;
2use std::fmt;
3use std::time::Duration;
4
5use crate::{Kind, SurrealValue, ToSql, Value};
6
7#[allow(missing_docs)]
13mod code {
14 pub const PARSE_ERROR: i64 = -32700;
15 pub const INVALID_REQUEST: i64 = -32600;
16 pub const METHOD_NOT_FOUND: i64 = -32601;
17 pub const METHOD_NOT_ALLOWED: i64 = -32602;
18 pub const INVALID_PARAMS: i64 = -32603;
19 pub const LIVE_QUERY_NOT_SUPPORTED: i64 = -32604;
20 pub const BAD_LIVE_QUERY_CONFIG: i64 = -32605;
21 pub const BAD_GRAPHQL_CONFIG: i64 = -32606;
22 pub const INTERNAL_ERROR: i64 = -32000;
23 pub const CLIENT_SIDE_ERROR: i64 = -32001;
24 pub const INVALID_AUTH: i64 = -32002;
25 pub const QUERY_NOT_EXECUTED: i64 = -32003;
26 pub const QUERY_TIMEDOUT: i64 = -32004;
27 pub const QUERY_CANCELLED: i64 = -32005;
28 pub const QUERY_TRANSACTION_CONFLICT: i64 = -32009;
29 pub const THROWN: i64 = -32006;
30 pub const SERIALIZATION_ERROR: i64 = -32007;
31 pub const DESERIALIZATION_ERROR: i64 = -32008;
32}
33
34fn default_code() -> i64 {
36 code::INTERNAL_ERROR
37}
38
39#[derive(Debug, Clone, PartialEq, Eq, SurrealValue)]
55#[surreal(crate = "crate")]
56pub struct Error {
57 #[surreal(default = "default_code")]
59 code: i64,
60 message: String,
62 #[surreal(flatten)]
65 details: ErrorDetails,
66 #[surreal(default)]
69 cause: Option<Box<Error>>,
70}
71
72impl Error {
73 pub fn validation(message: String, details: impl Into<Option<ValidationError>>) -> Self {
77 let details = details.into();
78 let code = details
79 .as_ref()
80 .map(|d| match d {
81 ValidationError::Parse => code::PARSE_ERROR,
82 ValidationError::InvalidRequest => code::INVALID_REQUEST,
83 ValidationError::InvalidParams => code::INVALID_PARAMS,
84 ValidationError::NamespaceEmpty
85 | ValidationError::DatabaseEmpty
86 | ValidationError::InvalidParameter {
87 ..
88 }
89 | ValidationError::InvalidContent {
90 ..
91 }
92 | ValidationError::InvalidMerge {
93 ..
94 } => code::INVALID_REQUEST,
95 })
96 .unwrap_or(code::INTERNAL_ERROR);
97 Self {
98 message,
99 code,
100 details: ErrorDetails::Validation(details),
101 cause: None,
102 }
103 }
104
105 pub fn not_allowed(message: String, details: impl Into<Option<NotAllowedError>>) -> Self {
108 let details = details.into();
109 let code = details
110 .as_ref()
111 .map(|d| match d {
112 NotAllowedError::Auth(auth_error) => match auth_error {
113 AuthError::TokenExpired => code::INVALID_AUTH,
114 AuthError::SessionExpired => code::INTERNAL_ERROR,
115 AuthError::InvalidAuth
116 | AuthError::UnexpectedAuth
117 | AuthError::MissingUserOrPass
118 | AuthError::NoSigninTarget
119 | AuthError::InvalidPass
120 | AuthError::TokenMakingFailed
121 | AuthError::InvalidRole {
122 ..
123 }
124 | AuthError::NotAllowed {
125 ..
126 }
127 | AuthError::InvalidSignup => code::INVALID_AUTH,
128 },
129 NotAllowedError::Method {
130 ..
131 }
132 | NotAllowedError::Scripting
133 | NotAllowedError::Function {
134 ..
135 }
136 | NotAllowedError::Target {
137 ..
138 } => code::METHOD_NOT_ALLOWED,
139 })
140 .unwrap_or(code::INTERNAL_ERROR);
141 Self {
142 message,
143 code,
144 details: ErrorDetails::NotAllowed(details),
145 cause: None,
146 }
147 }
148
149 pub fn configuration(message: String, details: impl Into<Option<ConfigurationError>>) -> Self {
152 let details = details.into();
153 let code = details
154 .as_ref()
155 .map(|d| match d {
156 ConfigurationError::LiveQueryNotSupported => code::LIVE_QUERY_NOT_SUPPORTED,
157 ConfigurationError::BadLiveQueryConfig => code::BAD_LIVE_QUERY_CONFIG,
158 ConfigurationError::BadGraphqlConfig => code::BAD_GRAPHQL_CONFIG,
159 })
160 .unwrap_or(code::INTERNAL_ERROR);
161 Self {
162 message,
163 code,
164 details: ErrorDetails::Configuration(details),
165 cause: None,
166 }
167 }
168
169 pub fn thrown(message: String) -> Self {
171 Self {
172 message,
173 code: code::THROWN,
174 details: ErrorDetails::Thrown,
175 cause: None,
176 }
177 }
178
179 pub fn query(message: String, details: impl Into<Option<QueryError>>) -> Self {
182 let details = details.into();
183 let code = details
184 .as_ref()
185 .map(|d| match d {
186 QueryError::NotExecuted => code::QUERY_NOT_EXECUTED,
187 QueryError::TimedOut {
188 ..
189 } => code::QUERY_TIMEDOUT,
190 QueryError::Cancelled => code::QUERY_CANCELLED,
191 QueryError::TransactionConflict => code::QUERY_TRANSACTION_CONFLICT,
192 })
193 .unwrap_or(code::INTERNAL_ERROR);
194 Self {
195 message,
196 code,
197 details: ErrorDetails::Query(details),
198 cause: None,
199 }
200 }
201
202 pub fn serialization(message: String, details: impl Into<Option<SerializationError>>) -> Self {
205 let details = details.into();
206 let code = details
207 .as_ref()
208 .map(|d| match d {
209 SerializationError::Serialization => code::SERIALIZATION_ERROR,
210 SerializationError::Deserialization => code::DESERIALIZATION_ERROR,
211 })
212 .unwrap_or(code::INTERNAL_ERROR);
213 Self {
214 message,
215 code,
216 details: ErrorDetails::Serialization(details),
217 cause: None,
218 }
219 }
220
221 pub fn not_found(message: String, details: impl Into<Option<NotFoundError>>) -> Self {
225 let details = details.into();
226 let code = details
227 .as_ref()
228 .and_then(|d| match d {
229 NotFoundError::Method {
230 ..
231 } => Some(code::METHOD_NOT_FOUND),
232 _ => None,
233 })
234 .unwrap_or(code::INTERNAL_ERROR);
235 Self {
236 message,
237 code,
238 details: ErrorDetails::NotFound(details),
239 cause: None,
240 }
241 }
242
243 pub fn already_exists(message: String, details: impl Into<Option<AlreadyExistsError>>) -> Self {
245 let details = details.into();
246 Self {
247 message,
248 code: code::INTERNAL_ERROR,
249 details: ErrorDetails::AlreadyExists(details),
250 cause: None,
251 }
252 }
253
254 pub fn connection(message: String, details: impl Into<Option<ConnectionError>>) -> Self {
257 let details = details.into();
258 Self {
259 message,
260 code: code::CLIENT_SIDE_ERROR,
261 details: ErrorDetails::Connection(details),
262 cause: None,
263 }
264 }
265
266 pub fn internal(message: String) -> Self {
268 Self {
269 message,
270 code: code::INTERNAL_ERROR,
271 details: ErrorDetails::Internal,
272 cause: None,
273 }
274 }
275
276 fn context(message: String) -> Self {
278 Self {
279 code: code::INTERNAL_ERROR,
280 message,
281 details: ErrorDetails::Context,
282 cause: None,
283 }
284 }
285
286 #[doc(hidden)]
290 pub fn from_details(message: String, details: ErrorDetails) -> Self {
291 Self {
292 code: default_code(),
293 message,
294 details,
295 cause: None,
296 }
297 }
298
299 #[doc(hidden)]
302 pub fn from_details_with_cause(
303 message: String,
304 details: ErrorDetails,
305 cause: Option<Box<Error>>,
306 ) -> Self {
307 Self {
308 code: default_code(),
309 message,
310 details,
311 cause,
312 }
313 }
314
315 #[doc(hidden)]
319 pub fn from_parts(message: String, kind: Option<&str>, details: Option<Value>) -> Self {
320 let kind_str = kind.unwrap_or("Internal");
321 let typed_details = match details {
322 Some(v) => ErrorDetails::from_value_with_kind_str(kind_str, v)
323 .unwrap_or_else(|_| ErrorDetails::from_kind_str(kind_str)),
324 None => ErrorDetails::from_kind_str(kind_str),
325 };
326 Self {
327 code: default_code(),
328 message,
329 details: typed_details,
330 cause: None,
331 }
332 }
333
334 pub fn cause(&self) -> Option<&Error> {
336 self.cause.as_deref()
337 }
338
339 pub fn with_cause(mut self, cause: Error) -> Self {
341 self.cause = Some(Box::new(cause));
342 self
343 }
344
345 #[doc(hidden)]
353 #[allow(clippy::needless_pass_by_value)] pub fn from_anyhow_with_chain(error: anyhow::Error) -> Self {
355 Self::internal(error.to_string())
356 }
357
358 pub fn kind_str(&self) -> &'static str {
360 self.details.kind_str()
361 }
362
363 pub fn message(&self) -> &str {
365 &self.message
366 }
367
368 pub fn details(&self) -> &ErrorDetails {
370 &self.details
371 }
372
373 pub fn is_validation(&self) -> bool {
375 self.details.is_validation()
376 }
377
378 pub fn is_configuration(&self) -> bool {
380 self.details.is_configuration()
381 }
382
383 pub fn is_query(&self) -> bool {
385 self.details.is_query()
386 }
387
388 pub fn is_serialization(&self) -> bool {
390 self.details.is_serialization()
391 }
392
393 pub fn is_not_allowed(&self) -> bool {
395 self.details.is_not_allowed()
396 }
397
398 pub fn is_not_found(&self) -> bool {
400 self.details.is_not_found()
401 }
402
403 pub fn is_already_exists(&self) -> bool {
405 self.details.is_already_exists()
406 }
407
408 pub fn is_connection(&self) -> bool {
410 self.details.is_connection()
411 }
412
413 pub fn is_thrown(&self) -> bool {
415 self.details.is_thrown()
416 }
417
418 pub fn is_internal(&self) -> bool {
420 self.details.is_internal()
421 }
422
423 pub fn is_context(&self) -> bool {
425 self.details.is_context()
426 }
427
428 pub fn validation_details(&self) -> Option<&ValidationError> {
430 match &self.details {
431 ErrorDetails::Validation(d) => d.as_ref(),
432 _ => None,
433 }
434 }
435
436 pub fn not_allowed_details(&self) -> Option<&NotAllowedError> {
438 match &self.details {
439 ErrorDetails::NotAllowed(d) => d.as_ref(),
440 _ => None,
441 }
442 }
443
444 pub fn configuration_details(&self) -> Option<&ConfigurationError> {
447 match &self.details {
448 ErrorDetails::Configuration(d) => d.as_ref(),
449 _ => None,
450 }
451 }
452
453 pub fn serialization_details(&self) -> Option<&SerializationError> {
456 match &self.details {
457 ErrorDetails::Serialization(d) => d.as_ref(),
458 _ => None,
459 }
460 }
461
462 pub fn not_found_details(&self) -> Option<&NotFoundError> {
464 match &self.details {
465 ErrorDetails::NotFound(d) => d.as_ref(),
466 _ => None,
467 }
468 }
469
470 pub fn query_details(&self) -> Option<&QueryError> {
472 match &self.details {
473 ErrorDetails::Query(d) => d.as_ref(),
474 _ => None,
475 }
476 }
477
478 pub fn already_exists_details(&self) -> Option<&AlreadyExistsError> {
481 match &self.details {
482 ErrorDetails::AlreadyExists(d) => d.as_ref(),
483 _ => None,
484 }
485 }
486
487 pub fn connection_details(&self) -> Option<&ConnectionError> {
489 match &self.details {
490 ErrorDetails::Connection(d) => d.as_ref(),
491 _ => None,
492 }
493 }
494}
495
496#[derive(Clone, Debug, PartialEq, Eq, SurrealValue)]
514#[non_exhaustive]
515#[surreal(crate = "crate")]
516#[surreal(tag = "kind", content = "details", skip_content_if = "Value::is_empty")]
517pub enum ErrorDetails {
518 Validation(Option<ValidationError>),
520 Configuration(Option<ConfigurationError>),
522 Query(Option<QueryError>),
524 Serialization(Option<SerializationError>),
526 NotAllowed(Option<NotAllowedError>),
528 NotFound(Option<NotFoundError>),
530 AlreadyExists(Option<AlreadyExistsError>),
532 Connection(Option<ConnectionError>),
534 Thrown,
536 #[surreal(other)]
539 Internal,
540 Context,
542}
543
544impl ErrorDetails {
545 pub fn kind_str(&self) -> &'static str {
547 match self {
548 Self::Validation(_) => "Validation",
549 Self::Configuration(_) => "Configuration",
550 Self::Query(_) => "Query",
551 Self::Serialization(_) => "Serialization",
552 Self::NotAllowed(_) => "NotAllowed",
553 Self::NotFound(_) => "NotFound",
554 Self::AlreadyExists(_) => "AlreadyExists",
555 Self::Connection(_) => "Connection",
556 Self::Thrown => "Thrown",
557 Self::Internal => "Internal",
558 Self::Context => "Context",
559 }
560 }
561
562 pub fn is_validation(&self) -> bool {
564 matches!(self, Self::Validation(_))
565 }
566 pub fn is_configuration(&self) -> bool {
568 matches!(self, Self::Configuration(_))
569 }
570 pub fn is_query(&self) -> bool {
572 matches!(self, Self::Query(_))
573 }
574 pub fn is_serialization(&self) -> bool {
576 matches!(self, Self::Serialization(_))
577 }
578 pub fn is_not_allowed(&self) -> bool {
580 matches!(self, Self::NotAllowed(_))
581 }
582 pub fn is_not_found(&self) -> bool {
584 matches!(self, Self::NotFound(_))
585 }
586 pub fn is_already_exists(&self) -> bool {
588 matches!(self, Self::AlreadyExists(_))
589 }
590 pub fn is_connection(&self) -> bool {
592 matches!(self, Self::Connection(_))
593 }
594 pub fn is_thrown(&self) -> bool {
596 matches!(self, Self::Thrown)
597 }
598 pub fn is_internal(&self) -> bool {
600 matches!(self, Self::Internal)
601 }
602 pub fn is_context(&self) -> bool {
604 matches!(self, Self::Context)
605 }
606
607 pub(crate) fn from_kind_str(kind: &str) -> Self {
610 match kind {
611 "Validation" => Self::Validation(None),
612 "Configuration" => Self::Configuration(None),
613 "Query" => Self::Query(None),
614 "Serialization" => Self::Serialization(None),
615 "NotAllowed" => Self::NotAllowed(None),
616 "NotFound" => Self::NotFound(None),
617 "AlreadyExists" => Self::AlreadyExists(None),
618 "Connection" => Self::Connection(None),
619 "Thrown" => Self::Thrown,
620 "Context" => Self::Context,
621 _ => Self::Internal,
623 }
624 }
625
626 pub(crate) fn from_value_with_kind_str(kind: &str, value: Value) -> Result<Self, Error> {
629 match kind {
630 "Validation" => {
631 ValidationError::from_value(value).map(|v| ErrorDetails::Validation(Some(v)))
632 }
633 "Configuration" => {
634 ConfigurationError::from_value(value).map(|v| ErrorDetails::Configuration(Some(v)))
635 }
636 "Query" => QueryError::from_value(value).map(|v| ErrorDetails::Query(Some(v))),
637 "Serialization" => {
638 SerializationError::from_value(value).map(|v| ErrorDetails::Serialization(Some(v)))
639 }
640 "NotAllowed" => {
641 NotAllowedError::from_value(value).map(|v| ErrorDetails::NotAllowed(Some(v)))
642 }
643 "NotFound" => NotFoundError::from_value(value).map(|v| ErrorDetails::NotFound(Some(v))),
644 "AlreadyExists" => {
645 AlreadyExistsError::from_value(value).map(|v| ErrorDetails::AlreadyExists(Some(v)))
646 }
647 "Connection" => {
648 ConnectionError::from_value(value).map(|v| ErrorDetails::Connection(Some(v)))
649 }
650 "Thrown" => Ok(Self::Thrown),
651 "Context" => Ok(Self::Context),
652 _ => Ok(Self::Internal),
653 }
654 }
655
656 pub fn has_details(&self) -> bool {
658 match self {
659 Self::Validation(d) => d.is_some(),
660 Self::Configuration(d) => d.is_some(),
661 Self::Query(d) => d.is_some(),
662 Self::Serialization(d) => d.is_some(),
663 Self::NotAllowed(d) => d.is_some(),
664 Self::NotFound(d) => d.is_some(),
665 Self::AlreadyExists(d) => d.is_some(),
666 Self::Connection(d) => d.is_some(),
667 Self::Thrown | Self::Internal | Self::Context => false,
668 }
669 }
670}
671
672#[derive(Clone, Debug, PartialEq, Eq, SurrealValue)]
678#[surreal(crate = "crate")]
679#[surreal(tag = "kind", content = "details")]
680#[non_exhaustive]
681pub enum AuthError {
682 #[surreal(skip_content)]
684 TokenExpired,
685 #[surreal(skip_content)]
687 SessionExpired,
688 #[surreal(skip_content)]
690 InvalidAuth,
691 #[surreal(skip_content)]
693 UnexpectedAuth,
694 #[surreal(skip_content)]
696 MissingUserOrPass,
697 #[surreal(skip_content)]
699 NoSigninTarget,
700 #[surreal(skip_content)]
702 InvalidPass,
703 #[surreal(skip_content)]
705 TokenMakingFailed,
706 #[surreal(skip_content)]
708 InvalidSignup,
709 InvalidRole {
711 name: String,
713 },
714 NotAllowed {
716 actor: String,
718 action: String,
720 resource: String,
722 },
723}
724
725impl From<AuthError> for Option<NotAllowedError> {
726 fn from(auth_error: AuthError) -> Self {
727 Some(NotAllowedError::Auth(auth_error))
728 }
729}
730
731#[derive(Clone, Debug, PartialEq, Eq, SurrealValue)]
733#[surreal(crate = "crate")]
734#[surreal(tag = "kind", content = "details")]
735#[non_exhaustive]
736pub enum ValidationError {
737 #[surreal(skip_content)]
739 Parse,
740 #[surreal(skip_content)]
742 InvalidRequest,
743 #[surreal(skip_content)]
745 InvalidParams,
746 #[surreal(skip_content)]
748 NamespaceEmpty,
749 #[surreal(skip_content)]
751 DatabaseEmpty,
752 InvalidParameter {
754 name: String,
756 },
757 InvalidContent {
759 value: String,
761 },
762 InvalidMerge {
764 value: String,
766 },
767}
768
769#[derive(Clone, Debug, PartialEq, Eq, SurrealValue)]
771#[surreal(crate = "crate")]
772#[surreal(tag = "kind", content = "details")]
773#[non_exhaustive]
774pub enum NotAllowedError {
775 #[surreal(skip_content)]
777 Scripting,
778 Auth(AuthError),
780 Method {
782 name: String,
784 },
785 Function {
787 name: String,
789 },
790 Target {
792 name: String,
794 },
795}
796
797#[derive(Clone, Debug, PartialEq, Eq, SurrealValue)]
799#[surreal(crate = "crate")]
800#[surreal(tag = "kind", skip_content)]
801#[non_exhaustive]
802pub enum ConfigurationError {
803 LiveQueryNotSupported,
805 BadLiveQueryConfig,
807 BadGraphqlConfig,
809}
810
811#[derive(Clone, Debug, PartialEq, Eq, SurrealValue)]
813#[surreal(crate = "crate")]
814#[surreal(tag = "kind", skip_content)]
815#[non_exhaustive]
816pub enum SerializationError {
817 Serialization,
819 Deserialization,
821}
822
823#[derive(Clone, Debug, PartialEq, Eq, SurrealValue)]
825#[surreal(crate = "crate")]
826#[surreal(tag = "kind", content = "details")]
827#[non_exhaustive]
828pub enum NotFoundError {
829 Method {
831 name: String,
833 },
834 Session {
836 id: Option<String>,
838 },
839 Table {
841 name: String,
843 },
844 Record {
846 id: String,
848 },
849 Namespace {
851 name: String,
853 },
854 Database {
856 name: String,
858 },
859 #[surreal(skip_content)]
861 Transaction,
862}
863
864#[derive(Clone, Debug, PartialEq, Eq, SurrealValue)]
866#[surreal(crate = "crate")]
867#[surreal(tag = "kind", content = "details")]
868#[non_exhaustive]
869pub enum QueryError {
870 #[surreal(skip_content)]
872 NotExecuted,
873 TimedOut {
875 duration: Duration,
877 },
878 #[surreal(skip_content)]
880 Cancelled,
881 #[surreal(skip_content)]
883 TransactionConflict,
884}
885
886#[derive(Clone, Debug, PartialEq, Eq, SurrealValue)]
888#[surreal(crate = "crate")]
889#[surreal(tag = "kind", content = "details")]
890#[non_exhaustive]
891pub enum AlreadyExistsError {
892 Session {
894 id: String,
896 },
897 Table {
899 name: String,
901 },
902 Record {
904 id: String,
906 },
907 Namespace {
909 name: String,
911 },
912 Database {
914 name: String,
916 },
917}
918
919#[derive(Clone, Debug, PartialEq, Eq, SurrealValue)]
922#[surreal(crate = "crate")]
923#[surreal(tag = "kind", skip_content)]
924#[non_exhaustive]
925pub enum ConnectionError {
926 Uninitialised,
928 AlreadyConnected,
930 ConnectionFailed,
932}
933
934impl fmt::Display for Error {
935 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
936 write!(f, "{}", self.message)
937 }
938}
939
940impl std::error::Error for Error {
941 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
942 self.cause.as_deref().map(|e| e as &(dyn std::error::Error + 'static))
943 }
944}
945
946pub trait Chain<T, E> {
964 fn chain<C>(self, context: C) -> Result<T, Error>
966 where
967 C: fmt::Display + Send + Sync + 'static;
968
969 fn chain_with<C, F>(self, f: F) -> Result<T, Error>
971 where
972 C: fmt::Display + Send + Sync + 'static,
973 F: FnOnce() -> C;
974}
975
976impl<T, E> Chain<T, E> for Result<T, E>
977where
978 E: Into<Error>,
979{
980 fn chain<C>(self, context: C) -> Result<T, Error>
981 where
982 C: fmt::Display + Send + Sync + 'static,
983 {
984 self.map_err(|e| Error::context(context.to_string()).with_cause(e.into()))
985 }
986
987 fn chain_with<C, F>(self, f: F) -> Result<T, Error>
988 where
989 C: fmt::Display + Send + Sync + 'static,
990 F: FnOnce() -> C,
991 {
992 self.map_err(|e| Error::context(f().to_string()).with_cause(e.into()))
993 }
994}
995
996impl<T> Chain<T, Infallible> for Option<T> {
997 fn chain<C>(self, context: C) -> Result<T, Error>
998 where
999 C: fmt::Display + Send + Sync + 'static,
1000 {
1001 self.ok_or_else(|| Error::context(context.to_string()))
1002 }
1003
1004 fn chain_with<C, F>(self, f: F) -> Result<T, Error>
1005 where
1006 C: fmt::Display + Send + Sync + 'static,
1007 F: FnOnce() -> C,
1008 {
1009 self.ok_or_else(|| Error::context(f().to_string()))
1010 }
1011}
1012
1013impl From<std::io::Error> for Error {
1020 fn from(error: std::io::Error) -> Self {
1021 use std::io::ErrorKind;
1022 let msg = error.to_string();
1023 match error.kind() {
1024 ErrorKind::NotFound => Error::not_found(msg, None),
1025 ErrorKind::AlreadyExists => Error::already_exists(msg, None),
1026 ErrorKind::PermissionDenied
1027 | ErrorKind::ReadOnlyFilesystem
1028 | ErrorKind::ResourceBusy
1029 | ErrorKind::ExecutableFileBusy => Error::not_allowed(msg, None),
1030 ErrorKind::ConnectionRefused
1031 | ErrorKind::ConnectionReset
1032 | ErrorKind::ConnectionAborted
1033 | ErrorKind::NotConnected
1034 | ErrorKind::HostUnreachable
1035 | ErrorKind::NetworkUnreachable
1036 | ErrorKind::NetworkDown
1037 | ErrorKind::TimedOut
1038 | ErrorKind::BrokenPipe
1039 | ErrorKind::AddrInUse
1040 | ErrorKind::AddrNotAvailable
1041 | ErrorKind::StaleNetworkFileHandle => Error::connection(msg, ConnectionError::ConnectionFailed),
1042 ErrorKind::InvalidData | ErrorKind::UnexpectedEof => {
1043 Error::serialization(msg, SerializationError::Deserialization)
1044 }
1045 ErrorKind::InvalidInput
1046 | ErrorKind::InvalidFilename
1047 | ErrorKind::NotADirectory
1048 | ErrorKind::IsADirectory
1049 | ErrorKind::DirectoryNotEmpty => Error::validation(msg, None),
1050 _ => Error::internal(msg),
1051 }
1052 }
1053}
1054
1055#[cfg(feature = "convert")]
1056impl From<async_channel::RecvError> for Error {
1057 fn from(error: async_channel::RecvError) -> Self {
1058 Error::connection(
1059 format!("Channel receive error: {}", error),
1060 ConnectionError::ConnectionFailed,
1061 )
1062 }
1063}
1064
1065#[cfg(feature = "convert")]
1066impl From<url::ParseError> for Error {
1067 fn from(error: url::ParseError) -> Self {
1068 Error::validation(error.to_string(), ValidationError::InvalidRequest)
1069 }
1070}
1071
1072#[cfg(feature = "convert")]
1073impl From<semver::Error> for Error {
1074 fn from(error: semver::Error) -> Self {
1075 Error::validation(error.to_string(), ValidationError::InvalidParams)
1076 }
1077}
1078
1079#[cfg(feature = "reqwest")]
1080impl From<reqwest::Error> for Error {
1081 fn from(error: reqwest::Error) -> Self {
1082 Error::connection(error.to_string(), ConnectionError::ConnectionFailed)
1083 }
1084}
1085
1086#[derive(Debug, Clone)]
1092#[non_exhaustive]
1093pub enum TypeError {
1094 Conversion(ConversionError),
1096 OutOfRange(OutOfRangeError),
1098 LengthMismatch(LengthMismatchError),
1100 Invalid(String),
1102}
1103
1104#[derive(Debug, Clone)]
1106#[non_exhaustive]
1107pub struct ConversionError {
1108 pub expected: Kind,
1110 pub actual: Kind,
1112 pub context: Option<String>,
1114}
1115
1116#[derive(Debug, Clone)]
1118#[non_exhaustive]
1119pub struct OutOfRangeError {
1120 pub value: String,
1122 pub target_type: String,
1124 pub context: Option<String>,
1126}
1127
1128#[derive(Debug, Clone)]
1130#[non_exhaustive]
1131pub struct LengthMismatchError {
1132 pub expected: usize,
1134 pub actual: usize,
1136 pub target_type: String,
1138}
1139
1140impl ConversionError {
1141 pub fn new(expected: Kind, actual: Kind) -> Self {
1143 Self {
1144 expected,
1145 actual,
1146 context: None,
1147 }
1148 }
1149
1150 pub fn from_value(expected: Kind, value: &Value) -> Self {
1152 Self {
1153 expected,
1154 actual: value.kind(),
1155 context: None,
1156 }
1157 }
1158
1159 pub fn with_context(mut self, context: impl Into<String>) -> Self {
1161 self.context = Some(context.into());
1162 self
1163 }
1164}
1165
1166impl OutOfRangeError {
1167 pub fn new(value: impl fmt::Display, target_type: impl Into<String>) -> Self {
1169 Self {
1170 value: value.to_string(),
1171 target_type: target_type.into(),
1172 context: None,
1173 }
1174 }
1175
1176 pub fn with_context(mut self, context: impl Into<String>) -> Self {
1178 self.context = Some(context.into());
1179 self
1180 }
1181}
1182
1183impl LengthMismatchError {
1184 pub fn new(expected: usize, actual: usize, target_type: impl Into<String>) -> Self {
1186 Self {
1187 expected,
1188 actual,
1189 target_type: target_type.into(),
1190 }
1191 }
1192}
1193
1194impl fmt::Display for TypeError {
1195 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1196 match self {
1197 TypeError::Conversion(e) => write!(f, "{e}"),
1198 TypeError::OutOfRange(e) => write!(f, "{e}"),
1199 TypeError::LengthMismatch(e) => write!(f, "{e}"),
1200 TypeError::Invalid(e) => write!(f, "Invalid: {e}"),
1201 }
1202 }
1203}
1204
1205impl fmt::Display for ConversionError {
1206 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1207 write!(f, "Expected {}, got {}", self.expected.to_sql(), self.actual.to_sql())?;
1208 if let Some(context) = &self.context {
1209 write!(f, " ({})", context)?;
1210 }
1211 Ok(())
1212 }
1213}
1214
1215impl fmt::Display for OutOfRangeError {
1216 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1217 write!(f, "Value {} is out of range for type {}", self.value, self.target_type)?;
1218 if let Some(context) = &self.context {
1219 write!(f, " ({})", context)?;
1220 }
1221 Ok(())
1222 }
1223}
1224
1225impl fmt::Display for LengthMismatchError {
1226 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1227 write!(
1228 f,
1229 "Length mismatch for {}: expected {}, got {}",
1230 self.target_type, self.expected, self.actual
1231 )
1232 }
1233}
1234
1235impl std::error::Error for TypeError {}
1236impl std::error::Error for ConversionError {}
1237impl std::error::Error for OutOfRangeError {}
1238impl std::error::Error for LengthMismatchError {}
1239
1240impl From<ConversionError> for Error {
1241 fn from(e: ConversionError) -> Self {
1242 Error::internal(e.to_string())
1243 }
1244}
1245
1246impl From<OutOfRangeError> for Error {
1247 fn from(e: OutOfRangeError) -> Self {
1248 Error::internal(e.to_string())
1249 }
1250}
1251
1252impl From<LengthMismatchError> for Error {
1253 fn from(e: LengthMismatchError) -> Self {
1254 Error::internal(e.to_string())
1255 }
1256}
1257
1258impl From<TypeError> for Error {
1259 fn from(e: TypeError) -> Self {
1260 Error::internal(e.to_string())
1261 }
1262}
1263
1264pub fn conversion_error(expected: Kind, value: impl Into<Value>) -> Error {
1266 let value = value.into();
1267 ConversionError::from_value(expected, &value).into()
1268}
1269
1270pub fn out_of_range_error(value: impl fmt::Display, target_type: impl Into<String>) -> Error {
1272 OutOfRangeError::new(value, target_type).into()
1273}
1274
1275pub fn length_mismatch_error(
1277 expected: usize,
1278 actual: usize,
1279 target_type: impl Into<String>,
1280) -> Error {
1281 LengthMismatchError::new(expected, actual, target_type).into()
1282}
1283
1284pub fn union_conversion_error(expected: Kind, value: impl Into<Value>) -> Error {
1287 let value = value.into();
1288 ConversionError::from_value(expected, &value)
1289 .with_context("Value does not match any variant in union type")
1290 .into()
1291}