async-graphql/src/error.rs

360 lines
8.2 KiB
Rust
Raw Normal View History

use graphql_parser::query::{ParseError, Value};
2020-03-01 10:54:34 +00:00
use graphql_parser::Pos;
use std::fmt::Debug;
/// FieldError type
2020-04-02 04:57:53 +00:00
#[derive(Clone, Debug)]
pub struct FieldError(pub String, pub Option<serde_json::Value>);
impl FieldError {
#[doc(hidden)]
pub fn into_error(self, pos: Pos) -> Error {
Error::Query {
pos,
path: None,
err: QueryError::FieldError {
err: self.0,
extended_error: self.1,
},
}
}
2020-03-01 10:54:34 +00:00
#[doc(hidden)]
pub fn into_error_with_path(self, pos: Pos, path: serde_json::Value) -> Error {
Error::Query {
pos,
path: Some(path),
err: QueryError::FieldError {
err: self.0,
extended_error: self.1,
},
}
}
}
/// FieldResult type
pub type FieldResult<T> = std::result::Result<T, FieldError>;
impl<E> From<E> for FieldError
where
2020-04-02 04:57:53 +00:00
E: std::fmt::Display + Send + Sync + 'static,
{
fn from(err: E) -> Self {
2020-04-02 04:57:53 +00:00
FieldError(format!("{}", err), None)
}
}
#[allow(missing_docs)]
pub trait ErrorExtensions
where
Self: Sized,
{
fn extend(&self) -> FieldError;
fn extend_with<C>(self, cb: C) -> FieldError
where
C: FnOnce(&Self) -> serde_json::Value,
{
let name = self.extend().0;
if let Some(mut base) = self.extend().1 {
let mut cb_res = cb(&self);
if let Some(base_map) = base.as_object_mut() {
if let Some(cb_res_map) = cb_res.as_object_mut() {
base_map.append(cb_res_map);
}
return FieldError(name, Some(serde_json::json!(base_map)));
} else {
return FieldError(name, Some(cb_res));
}
}
FieldError(name, Some(cb(&self)))
}
}
impl ErrorExtensions for FieldError {
fn extend(&self) -> FieldError {
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<E: std::fmt::Display> ErrorExtensions for &E {
fn extend(&self) -> FieldError {
FieldError(format!("{}", self), None)
}
}
#[allow(missing_docs)]
pub trait ResultExt<T, E>
where
Self: Sized,
{
fn extend_err<CB>(self, cb: CB) -> FieldResult<T>
where
CB: FnOnce(&E) -> serde_json::Value;
2020-04-02 04:57:53 +00:00
fn extend(self) -> FieldResult<T>;
}
2020-04-02 04:57:53 +00:00
// This is implemented on E and not &E which means it cannot be used on foreign types.
// (see example).
impl<T, E> ResultExt<T, E> for std::result::Result<T, E>
where
2020-04-02 04:57:53 +00:00
E: ErrorExtensions + Send + Sync + 'static,
{
fn extend_err<C>(self, cb: C) -> FieldResult<T>
where
C: FnOnce(&E) -> serde_json::Value,
{
match self {
2020-04-02 04:57:53 +00:00
Err(err) => Err(err.extend_with(|e| cb(e))),
Ok(value) => Ok(value),
}
}
fn extend(self) -> FieldResult<T> {
match self {
Err(err) => Err(err.extend()),
Ok(value) => Ok(value),
}
}
}
2020-03-01 10:54:34 +00:00
2020-03-20 03:56:08 +00:00
/// Error for query
2020-03-01 10:54:34 +00:00
#[derive(Debug, Error)]
2020-03-20 03:56:08 +00:00
#[allow(missing_docs)]
2020-03-01 13:35:39 +00:00
pub enum QueryError {
2020-03-01 10:54:34 +00:00
#[error("Not supported.")]
NotSupported,
#[error("Expected type \"{expect}\", found {actual}.")]
2020-03-20 03:56:08 +00:00
ExpectedType {
/// Expect input type
expect: String,
/// Actual input type
actual: Value,
},
2020-03-01 10:54:34 +00:00
2020-03-01 16:52:05 +00:00
#[error("Expected type \"{expect}\", found {actual}.")]
ExpectedJsonType {
2020-03-20 03:56:08 +00:00
/// Expect input JSON type
2020-03-03 11:15:18 +00:00
expect: String,
2020-03-20 03:56:08 +00:00
/// Actual input JSON type
2020-03-01 16:52:05 +00:00
actual: serde_json::Value,
},
2020-03-01 10:54:34 +00:00
#[error("Cannot query field \"{field_name}\" on type \"{object}\".")]
2020-03-20 03:56:08 +00:00
FieldNotFound {
/// Field name
field_name: String,
/// Object name
object: String,
},
2020-03-01 10:54:34 +00:00
2020-03-14 03:46:20 +00:00
#[error("Missing operation")]
MissingOperation,
2020-03-01 10:54:34 +00:00
#[error("Unknown operation named \"{name}\"")]
2020-03-20 03:56:08 +00:00
UnknownOperationNamed {
/// Operation name for query
name: String,
},
2020-03-01 10:54:34 +00:00
#[error("Type \"{object}\" must have a selection of subfields.")]
2020-03-20 03:56:08 +00:00
MustHaveSubFields {
/// Object name
object: String,
},
2020-03-01 10:54:34 +00:00
#[error("Schema is not configured for mutations.")]
NotConfiguredMutations,
2020-03-17 09:26:59 +00:00
#[error("Schema is not configured for subscriptions.")]
NotConfiguredSubscriptions,
2020-03-02 00:24:49 +00:00
#[error("Invalid value for enum \"{ty}\".")]
2020-03-20 03:56:08 +00:00
InvalidEnumValue {
/// Enum type name
ty: String,
/// Enum value
value: String,
},
2020-03-01 13:35:39 +00:00
#[error("Required field \"{field_name}\" for InputObject \"{object}\" does not exist.")]
RequiredField {
2020-03-20 03:56:08 +00:00
/// field name
2020-03-01 13:35:39 +00:00
field_name: String,
2020-03-20 03:56:08 +00:00
/// object name
2020-03-01 13:35:39 +00:00
object: &'static str,
},
#[error("Variable \"${var_name}\" is not defined")]
2020-03-20 03:56:08 +00:00
VarNotDefined {
/// Variable name
var_name: String,
},
#[error(
"Directive \"{directive}\" argument \"{arg_name}\" of type \"{arg_type}\" is required, but it was not provided."
)]
RequiredDirectiveArgs {
2020-03-20 03:56:08 +00:00
/// Directive name
directive: &'static str,
2020-03-20 03:56:08 +00:00
/// Argument name
arg_name: &'static str,
2020-03-20 03:56:08 +00:00
/// Argument type
arg_type: &'static str,
},
#[error("Unknown directive \"{name}\".")]
2020-03-20 03:56:08 +00:00
UnknownDirective {
/// Directive name
name: String,
},
2020-03-05 07:50:57 +00:00
#[error("Unknown fragment \"{name}\".")]
2020-03-20 03:56:08 +00:00
UnknownFragment {
// Fragment name
name: String,
},
2020-03-07 02:39:55 +00:00
#[error("Object \"{object}\" does not implement interface \"{interface}\"")]
2020-03-20 03:56:08 +00:00
NotImplementedInterface {
/// Object name
object: String,
/// Interface name
interface: String,
},
2020-03-07 02:39:55 +00:00
#[error("Unrecognized inline fragment \"{name}\" on type \"{object}\"")]
2020-03-20 03:56:08 +00:00
UnrecognizedInlineFragment {
/// Object name
object: String,
/// Inline fragment name
name: String,
},
2020-03-19 09:20:12 +00:00
#[error("Too complex")]
2020-03-25 07:07:16 +00:00
TooComplex,
#[error("Too deep")]
2020-03-25 07:07:16 +00:00
TooDeep,
2020-03-01 10:54:34 +00:00
#[error("Failed to resolve field: {err}")]
FieldError {
2020-04-02 04:57:53 +00:00
err: String,
extended_error: Option<serde_json::Value>,
},
2020-04-09 14:03:09 +00:00
#[error("Entity not found")]
EntityNotFound,
#[error("\"__typename\" must be an existing string")]
TypeNameNotExists,
2020-03-01 10:54:34 +00:00
}
impl QueryError {
#[doc(hidden)]
pub fn into_error(self, pos: Pos) -> Error {
Error::Query {
pos,
path: None,
err: self,
2020-03-01 10:54:34 +00:00
}
}
}
2020-03-08 12:35:36 +00:00
#[derive(Debug)]
pub struct RuleError {
pub locations: Vec<Pos>,
pub message: String,
}
impl From<ParseError> for Error {
fn from(err: ParseError) -> Self {
let msg = err.to_string();
2020-04-02 04:57:53 +00:00
let mut s = msg.splitn(2, '\n');
let first = s.next().unwrap();
2020-04-02 04:57:53 +00:00
let ln = &first[first.rfind(' ').unwrap() + 1..];
let (line, column) = {
2020-04-02 04:57:53 +00:00
let mut s = ln.splitn(2, ':');
(
s.next().unwrap().parse().unwrap(),
s.next().unwrap().parse().unwrap(),
)
};
let tail = s.next().unwrap();
Error::Parse {
line,
column,
message: tail.to_string(),
2020-03-08 12:35:36 +00:00
}
}
}
#[allow(missing_docs)]
#[derive(Debug, Error)]
pub enum RequestError {
#[error("{0}")]
Io(std::io::Error),
#[error("Invalid request: {0}")]
InvalidRequest(serde_json::Error),
#[error("Invalid files map: {0}")]
InvalidFilesMap(serde_json::Error),
#[error("Invalid multipart data: {0}")]
InvalidMultipart(std::io::Error),
#[error("Missing \"operators\" part")]
MissingOperatorsPart,
#[error("Missing \"map\" part")]
MissingMapPart,
#[error("Failed to read part data: {0}")]
PartData(#[from] std::io::Error),
#[error("It's not an upload operation")]
NotUpload,
#[error("Missing files")]
MissingFiles,
}
#[allow(missing_docs)]
#[derive(Debug, Error)]
pub enum Error {
#[error("Parse error: {message}")]
Parse {
line: usize,
column: usize,
message: String,
},
#[error("Query error: {err}")]
Query {
pos: Pos,
path: Option<serde_json::Value>,
err: QueryError,
},
#[error("Rule error")]
Rule { errors: Vec<RuleError> },
#[error("Request error")]
Request(#[from] RequestError),
}