Skip to content

Implement std::error::Error for all error types #419

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Feb 21, 2020
Merged
8 changes: 8 additions & 0 deletions juniper/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
8 changes: 7 additions & 1 deletion juniper/src/executor/mod.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down
21 changes: 21 additions & 0 deletions juniper/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,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},
Expand Down Expand Up @@ -193,6 +194,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,
Expand Down
2 changes: 2 additions & 0 deletions juniper/src/parser/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -538,3 +538,5 @@ impl fmt::Display for LexerError {
}
}
}

impl std::error::Error for LexerError {}
2 changes: 2 additions & 0 deletions juniper/src/parser/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,3 +203,5 @@ impl<'a> fmt::Display for ParseError<'a> {
}
}
}

impl<'a> std::error::Error for ParseError<'a> {}
14 changes: 14 additions & 0 deletions juniper/src/parser/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
}
}
20 changes: 19 additions & 1 deletion juniper/src/validation/context.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use std::{collections::HashSet, fmt::Debug};
use std::{
collections::HashSet,
fmt::{self, Debug},
};

use crate::ast::{Definition, Document, Type};

Expand Down Expand Up @@ -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> {
Expand Down
119 changes: 119 additions & 0 deletions juniper/src/value/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::{
ast::{InputValue, ToInputValue},
parser::Spanning,
};
use std::fmt::{self, Display, Formatter};
mod object;
mod scalar;

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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));
}
}