Implement std::error::Error
for all error types (#419)
* Implement `std::error::Error` for all error types * Fix copy-paste * Implement `Display` for `Value` This is required for implementing `Display` for `FieldError` * Implement `std::error::Error` for `FieldError` This required removing `impl From<T> for FieldError where T: Display` because it would otherwise cause a conflicting implementation. That is because `impl From<T> for T` already exists. Instead I added `impl From<String> for FieldError` and `impl From<&str> for FieldError` which should cover most use cases of the previous `impl`. I also added `FieldError::from_error` so users can convert from any error they may have. * Bring back `impl<T: Display, S> From<T> for FieldError<S>` We cannot have this and `impl<S> std::error::Error for FieldError<S>` so we agreed this is more valuable. More context https://github.com/graphql-rust/juniper/pull/419 * Write errors without allocations
This commit is contained in:
parent
db68dd7697
commit
ca28e90f7e
8 changed files with 192 additions and 2 deletions
|
@ -4,6 +4,14 @@
|
|||
|
||||
- Support raw identifiers in field and argument names. (#[object] macro)
|
||||
|
||||
- Most error types now implement `std::error::Error`:
|
||||
- `GraphQLError`
|
||||
- `LexerError`
|
||||
- `ParseError`
|
||||
- `RuleError`
|
||||
|
||||
See [#419](https://github.com/graphql-rust/juniper/pull/419).
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- remove old `graphql_object!` macro, rename `object` proc macro to `graphql_object`
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
use std::{borrow::Cow, cmp::Ordering, collections::HashMap, fmt::Display, sync::RwLock};
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
cmp::Ordering,
|
||||
collections::HashMap,
|
||||
fmt::{self, Debug, Display},
|
||||
sync::RwLock,
|
||||
};
|
||||
|
||||
use fnv::FnvHashMap;
|
||||
|
||||
|
|
|
@ -162,6 +162,7 @@ use crate::{
|
|||
parser::{parse_document_source, ParseError, Spanning},
|
||||
validation::{validate_input_values, visit_all_rules, ValidatorContext},
|
||||
};
|
||||
use std::fmt;
|
||||
|
||||
pub use crate::{
|
||||
ast::{FromInputValue, InputValue, Selection, ToInputValue, Type},
|
||||
|
@ -198,6 +199,26 @@ pub enum GraphQLError<'a> {
|
|||
IsSubscription,
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for GraphQLError<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
GraphQLError::ParseError(error) => write!(f, "{}", error),
|
||||
GraphQLError::ValidationError(errors) => {
|
||||
for error in errors {
|
||||
writeln!(f, "{}", error)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
GraphQLError::NoOperationProvided => write!(f, "No operation provided"),
|
||||
GraphQLError::MultipleOperationsProvided => write!(f, "Multiple operations provided"),
|
||||
GraphQLError::UnknownOperationName => write!(f, "Unknown operation name"),
|
||||
GraphQLError::IsSubscription => write!(f, "Subscription are not currently supported"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> std::error::Error for GraphQLError<'a> {}
|
||||
|
||||
/// Execute a query in a provided schema
|
||||
pub fn execute<'a, S, CtxT, QueryT, MutationT>(
|
||||
document_source: &'a str,
|
||||
|
|
|
@ -538,3 +538,5 @@ impl fmt::Display for LexerError {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for LexerError {}
|
||||
|
|
|
@ -203,3 +203,5 @@ impl<'a> fmt::Display for ParseError<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> std::error::Error for ParseError<'a> {}
|
||||
|
|
|
@ -90,6 +90,14 @@ impl<T> Spanning<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display> fmt::Display for Spanning<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}. At {}", self.item, self.start)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: std::error::Error> std::error::Error for Spanning<T> {}
|
||||
|
||||
impl SourcePosition {
|
||||
#[doc(hidden)]
|
||||
pub fn new(index: usize, line: usize, col: usize) -> SourcePosition {
|
||||
|
@ -142,3 +150,9 @@ impl SourcePosition {
|
|||
self.col
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SourcePosition {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}:{}", self.line, self.col)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
use std::{collections::HashSet, fmt::Debug};
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
fmt::{self, Debug},
|
||||
};
|
||||
|
||||
use crate::ast::{Definition, Document, Type};
|
||||
|
||||
|
@ -48,6 +51,21 @@ impl RuleError {
|
|||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for RuleError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
// this is fine since all `RuleError`s should have at least one source position
|
||||
let locations = self
|
||||
.locations
|
||||
.iter()
|
||||
.map(|location| format!("{}", location))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
write!(f, "{}. At {}", self.message, locations)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for RuleError {}
|
||||
|
||||
impl<'a, S: Debug> ValidatorContext<'a, S> {
|
||||
#[doc(hidden)]
|
||||
pub fn new(schema: &'a SchemaType<S>, document: &Document<'a, S>) -> ValidatorContext<'a, S> {
|
||||
|
|
|
@ -2,6 +2,7 @@ use crate::{
|
|||
ast::{InputValue, ToInputValue},
|
||||
parser::Spanning,
|
||||
};
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
mod object;
|
||||
mod scalar;
|
||||
|
||||
|
@ -185,6 +186,46 @@ impl<S: ScalarValue> ToInputValue<S> for Value<S> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<S: ScalarValue> Display for Value<S> {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Value::Null => write!(f, "null"),
|
||||
Value::Scalar(s) => {
|
||||
if let Some(string) = s.as_string() {
|
||||
write!(f, "\"{}\"", string)
|
||||
} else {
|
||||
write!(f, "{}", s)
|
||||
}
|
||||
}
|
||||
Value::List(list) => {
|
||||
write!(f, "[")?;
|
||||
for (idx, item) in list.iter().enumerate() {
|
||||
write!(f, "{}", item)?;
|
||||
if idx < list.len() - 1 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
}
|
||||
write!(f, "]")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Value::Object(obj) => {
|
||||
write!(f, "{{")?;
|
||||
for (idx, (key, value)) in obj.iter().enumerate() {
|
||||
write!(f, "\"{}\": {}", key, value)?;
|
||||
|
||||
if idx < obj.field_count() - 1 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
}
|
||||
write!(f, "}}")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, T> From<Option<T>> for Value<S>
|
||||
where
|
||||
S: ScalarValue,
|
||||
|
@ -354,4 +395,82 @@ mod tests {
|
|||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn display_null() {
|
||||
let s: Value<DefaultScalarValue> = graphql_value!(None);
|
||||
assert_eq!("null", format!("{}", s));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn display_int() {
|
||||
let s: Value<DefaultScalarValue> = graphql_value!(123);
|
||||
assert_eq!("123", format!("{}", s));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn display_float() {
|
||||
let s: Value<DefaultScalarValue> = graphql_value!(123.456);
|
||||
assert_eq!("123.456", format!("{}", s));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn display_string() {
|
||||
let s: Value<DefaultScalarValue> = graphql_value!("foo");
|
||||
assert_eq!("\"foo\"", format!("{}", s));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn display_bool() {
|
||||
let s: Value<DefaultScalarValue> = graphql_value!(false);
|
||||
assert_eq!("false", format!("{}", s));
|
||||
|
||||
let s: Value<DefaultScalarValue> = graphql_value!(true);
|
||||
assert_eq!("true", format!("{}", s));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn display_list() {
|
||||
let s: Value<DefaultScalarValue> = graphql_value!([1, None, "foo"]);
|
||||
assert_eq!("[1, null, \"foo\"]", format!("{}", s));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn display_list_one_element() {
|
||||
let s: Value<DefaultScalarValue> = graphql_value!([1]);
|
||||
assert_eq!("[1]", format!("{}", s));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn display_list_empty() {
|
||||
let s: Value<DefaultScalarValue> = graphql_value!([]);
|
||||
assert_eq!("[]", format!("{}", s));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn display_object() {
|
||||
let s: Value<DefaultScalarValue> = graphql_value!({
|
||||
"int": 1,
|
||||
"null": None,
|
||||
"string": "foo",
|
||||
});
|
||||
assert_eq!(
|
||||
r#"{"int": 1, "null": null, "string": "foo"}"#,
|
||||
format!("{}", s)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn display_object_one_field() {
|
||||
let s: Value<DefaultScalarValue> = graphql_value!({
|
||||
"int": 1,
|
||||
});
|
||||
assert_eq!(r#"{"int": 1}"#, format!("{}", s));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn display_object_empty() {
|
||||
let s = Value::<DefaultScalarValue>::object(Object::with_capacity(0));
|
||||
assert_eq!(r#"{}"#, format!("{}", s));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue