use std::collections::BTreeMap; use std::fmt::{self, Debug, Display, Formatter}; use std::marker::PhantomData; use serde::{Deserialize, Serialize}; use thiserror::Error; use crate::{parser, InputType, Pos, Value}; /// Extensions to the error. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] #[serde(transparent)] pub struct ErrorExtensionValues(BTreeMap); impl ErrorExtensionValues { /// Set an extension value. pub fn set(&mut self, name: impl AsRef, value: impl Into) { self.0.insert(name.as_ref().to_string(), value.into()); } } /// An error in a GraphQL server. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct ServerError { /// An explanatory message of the error. pub message: String, /// Where the error occurred. #[serde(skip_serializing_if = "Vec::is_empty", default)] pub locations: Vec, /// If the error occurred in a resolver, the path to the error. #[serde(skip_serializing_if = "Vec::is_empty", default)] pub path: Vec, /// Extensions to the error. #[serde(skip_serializing_if = "error_extensions_is_empty", default)] pub extensions: Option, } fn error_extensions_is_empty(values: &Option) -> bool { values.as_ref().map_or(true, |values| values.0.is_empty()) } impl ServerError { /// Create a new server error with the message. pub fn new(message: impl Into) -> Self { Self { message: message.into(), locations: Vec::new(), path: Vec::new(), extensions: None, } } /// Add a position to the error. pub fn at(mut self, at: Pos) -> Self { self.locations.push(at); self } /// Prepend a path to the error. pub fn path(mut self, path: PathSegment) -> Self { self.path.insert(0, path); self } } impl Display for ServerError { fn fmt(&self, f: &mut Formatter) -> fmt::Result { f.write_str(&self.message) } } impl From for Vec { fn from(single: ServerError) -> Self { vec![single] } } impl From for ServerError { fn from(e: Error) -> Self { e.into_server_error() } } impl From for ServerError { fn from(e: parser::Error) -> Self { Self { message: e.to_string(), locations: e.positions().collect(), path: Vec::new(), extensions: None, } } } /// A segment of path to a resolver. /// /// This is like [`QueryPathSegment`](enum.QueryPathSegment.html), but owned and used as a part of /// errors instead of during execution. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(untagged)] pub enum PathSegment { /// A field in an object. Field(String), /// An index in a list. Index(usize), } /// Alias for `Result`. pub type ServerResult = std::result::Result; /// An error parsing an input value. /// /// This type is generic over T as it uses T's type name when converting to a regular error. #[derive(Debug)] pub struct InputValueError { message: String, phantom: PhantomData, } impl InputValueError { fn new(message: String) -> Self { Self { message, phantom: PhantomData, } } /// The expected input type did not match the actual input type. #[must_use] pub fn expected_type(actual: Value) -> Self { Self::new(format!( r#"Expected input type "{}", found {}."#, T::type_name(), actual )) } /// A custom error message. /// /// Any type that implements `Display` is automatically converted to this if you use the `?` /// operator. #[must_use] pub fn custom(msg: impl Display) -> Self { Self::new(format!(r#"Failed to parse "{}": {}"#, T::type_name(), msg)) } /// Propagate the error message to a different type. pub fn propagate(self) -> InputValueError { if T::type_name() != U::type_name() { InputValueError::new(format!( r#"{} (occurred while parsing "{}")"#, self.message, U::type_name() )) } else { InputValueError::new(self.message) } } /// Convert the error into a server error. pub fn into_server_error(self) -> ServerError { ServerError::new(self.message) } } impl From for InputValueError { fn from(error: E) -> Self { Self::custom(error) } } /// An error parsing a value of type `T`. pub type InputValueResult = Result>; /// An error with a message and optional extensions. #[derive(Debug, Clone, PartialEq, Eq, Serialize)] pub struct Error { /// The error message. pub message: String, /// Extensions to the error. #[serde(skip_serializing_if = "error_extensions_is_empty")] pub extensions: Option, } impl Error { /// Create an error from the given error message. pub fn new(message: impl Into) -> Self { Self { message: message.into(), extensions: None, } } /// Convert the error to a server error. #[must_use] pub fn into_server_error(self) -> ServerError { ServerError { message: self.message, locations: Vec::new(), path: Vec::new(), extensions: self.extensions, } } } impl From for Error { fn from(e: T) -> Self { Self { message: e.to_string(), extensions: None, } } } /// An alias for `Result`. pub type Result = std::result::Result; /// An error parsing the request. #[derive(Debug, Error)] #[non_exhaustive] pub enum ParseRequestError { /// An IO error occurred. #[error("{0}")] Io(#[from] std::io::Error), /// The request's syntax was invalid. #[error("Invalid request: {0}")] InvalidRequest(serde_json::Error), /// The request's files map was invalid. #[error("Invalid files map: {0}")] InvalidFilesMap(serde_json::Error), /// The request's multipart data was invalid. #[error("Invalid multipart data")] #[cfg(feature = "multipart")] #[cfg_attr(feature = "nightly", doc(cfg(feature = "multipart")))] InvalidMultipart(multer::Error), /// Missing "operators" part for multipart request. #[error("Missing \"operators\" part")] MissingOperatorsPart, /// Missing "map" part for multipart request. #[error("Missing \"map\" part")] MissingMapPart, /// It's not an upload operation #[error("It's not an upload operation")] NotUpload, /// Files were missing the request. #[error("Missing files")] MissingFiles, /// The request's payload is too large, and this server rejected it. #[error("Payload too large")] PayloadTooLarge, /// The request is a batch request, but the server does not support batch requests. #[error("Batch requests are not supported")] UnsupportedBatch, } #[cfg(feature = "multipart")] impl From for ParseRequestError { fn from(err: multer::Error) -> Self { match err { multer::Error::FieldSizeExceeded { .. } | multer::Error::StreamSizeExceeded { .. } => { ParseRequestError::PayloadTooLarge } _ => ParseRequestError::InvalidMultipart(err), } } } /// An error which can be extended into a `Error`. pub trait ErrorExtensions: Sized { /// Convert the error to a `Error`. fn extend(&self) -> Error; /// Add extensions to the error, using a callback to make the extensions. fn extend_with(self, cb: C) -> Error where C: FnOnce(&Self, &mut ErrorExtensionValues), { let message = self.extend().message; let mut extensions = self.extend().extensions.unwrap_or_default(); cb(&self, &mut extensions); Error { message, extensions: Some(extensions), } } } impl ErrorExtensions for Error { fn extend(&self) -> Error { self.clone() } } // implementing for &E instead of E gives the user the possibility to implement for E which does // not conflict with this implementation acting as a fallback. impl ErrorExtensions for &E { fn extend(&self) -> Error { Error { message: self.to_string(), extensions: None, } } } /// Extend a `Result`'s error value with [`ErrorExtensions`](trait.ErrorExtensions.html). pub trait ResultExt: Sized { /// Extend the error value of the result with the callback. fn extend_err(self, cb: C) -> Result where C: FnOnce(&E, &mut ErrorExtensionValues); /// Extend the result to a `Result`. fn extend(self) -> Result; } // This is implemented on E and not &E which means it cannot be used on foreign types. // (see example). impl ResultExt for std::result::Result where E: ErrorExtensions + Send + Sync + 'static, { fn extend_err(self, cb: C) -> Result where C: FnOnce(&E, &mut ErrorExtensionValues), { match self { Err(err) => Err(err.extend_with(|e, ee| cb(e, ee))), Ok(value) => Ok(value), } } fn extend(self) -> Result { match self { Err(err) => Err(err.extend()), Ok(value) => Ok(value), } } }