Skip to content

Commit

Permalink
Remove ValidationErrors type, generalize error utils
Browse files Browse the repository at this point in the history
Summary: Per offline discussion w kassens this changes the `try_*` error utilities to deal with `Vec<E>` (where each instance can have a different error type) instead of the more narrow `ValidationErrors` type. This makes it possible to use the error utilities with lists of syntax or other error types.

Reviewed By: kassens

Differential Revision: D19958009

fbshipit-source-id: 639d5a4bea0a494feadbc6c9016a411e342ea8a3
  • Loading branch information
josephsavona authored and facebook-github-bot committed Feb 19, 2020
1 parent 006be0a commit 5d733bc
Show file tree
Hide file tree
Showing 13 changed files with 210 additions and 255 deletions.
7 changes: 7 additions & 0 deletions compiler/crates/errors/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[project]
name = "errors"
version = "0.0.0"
edition = "2018"

[dependencies]
rayon = "1.3.0"
156 changes: 156 additions & 0 deletions compiler/crates/errors/src/error_combinators.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

use rayon::prelude::*;

// Helpers for unwrapping multiple Results to aggregate all success values or all error values.

pub fn try2<T1, T2, E>(t1: Result<T1, Vec<E>>, t2: Result<T2, Vec<E>>) -> Result<(T1, T2), Vec<E>> {
match (t1, t2) {
(Ok(t1), Ok(t2)) => Ok((t1, t2)),
(Ok(_), Err(errors)) => Err(errors),
(Err(errors), Ok(_)) => Err(errors),
(Err(mut errors1), Err(errors2)) => {
errors1.extend(errors2);
Err(errors1)
}
}
}

pub fn try3<T1, T2, T3, E>(
t1: Result<T1, Vec<E>>,
t2: Result<T2, Vec<E>>,
t3: Result<T3, Vec<E>>,
) -> Result<(T1, T2, T3), Vec<E>> {
let mut errors = Vec::new();
let t1 = match t1 {
Ok(t1) => Some(t1),
Err(e) => {
errors.extend(e);
None
}
};
let t2 = match t2 {
Ok(t2) => Some(t2),
Err(e) => {
errors.extend(e);
None
}
};
let t3 = match t3 {
Ok(t3) => Some(t3),
Err(e) => {
errors.extend(e);
None
}
};
if !errors.is_empty() {
Err(errors)
} else {
Ok((t1.unwrap(), t2.unwrap(), t3.unwrap()))
}
}

pub fn try4<T1, T2, T3, T4, E>(
t1: Result<T1, Vec<E>>,
t2: Result<T2, Vec<E>>,
t3: Result<T3, Vec<E>>,
t4: Result<T4, Vec<E>>,
) -> Result<(T1, T2, T3, T4), Vec<E>> {
let mut errors = Vec::new();
let t1 = match t1 {
Ok(t1) => Some(t1),
Err(e) => {
errors.extend(e);
None
}
};
let t2 = match t2 {
Ok(t2) => Some(t2),
Err(e) => {
errors.extend(e);
None
}
};
let t3 = match t3 {
Ok(t3) => Some(t3),
Err(e) => {
errors.extend(e);
None
}
};
let t4 = match t4 {
Ok(t4) => Some(t4),
Err(e) => {
errors.extend(e);
None
}
};
if !errors.is_empty() {
Err(errors)
} else {
Ok((t1.unwrap(), t2.unwrap(), t3.unwrap(), t4.unwrap()))
}
}

/// Given an iterable of Result values, returns all the success values if all items are
/// `Ok` or all the error values if one or more items were `Err`.
pub fn try_all<T, E, I>(items: I) -> Result<Vec<T>, Vec<E>>
where
I: IntoIterator<Item = Result<T, Vec<E>>>,
{
try_map(items, |x| x)
}

/// Transforms the items of a list with a fallible transform function, returning either
/// all the transformed values if all items succeeded or a list of all errors if one or
/// more items could not be tansformed.
pub fn try_map<T, E, U, I, F>(items: I, mut f: F) -> Result<Vec<T>, Vec<E>>
where
I: IntoIterator<Item = U>,
F: FnMut(U) -> Result<T, Vec<E>>,
{
let iter = items.into_iter();
let mut errors = Vec::new();
let mut values = Vec::with_capacity(iter.size_hint().1.unwrap_or_default());
for item in iter {
match f(item) {
Ok(item_value) => values.push(item_value),
Err(item_errors) => errors.extend(item_errors),
}
}
if errors.is_empty() {
Ok(values)
} else {
Err(errors)
}
}

/// Similar to `try_map` but performs the transform in parallel.
pub fn par_try_map<T: Sync + Send, E: Sync + Send, U: Sync + Send, I, F: Sync + Send>(
items: I,
f: F,
) -> Result<Vec<T>, Vec<E>>
where
I: IntoParallelIterator<Item = U>,
F: Fn(U) -> Result<T, Vec<E>>,
{
let results: Vec<Result<T, Vec<E>>> = items.into_par_iter().map(f).collect();
let mut errors = Vec::new();
let mut values = Vec::with_capacity(results.len());
for result in results {
match result {
Ok(item_value) => values.push(item_value),
Err(item_errors) => errors.extend(item_errors),
}
}
if errors.is_empty() {
Ok(values)
} else {
Err(errors)
}
}
10 changes: 10 additions & 0 deletions compiler/crates/errors/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

mod error_combinators;

pub use error_combinators::*;
2 changes: 1 addition & 1 deletion compiler/crates/graphql-ir/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ edition = "2018"

[dependencies]
common = { path = "../common" }
errors = { path = "../errors" }
fnv = "1.0.6"
graphql-syntax = { path = "../graphql-syntax" }
interner = { path = "../interner" }
lazy_static = "1.4.0"
rayon = "1.3.0"
schema = { path = "../schema" }
thiserror = "1.0.9"

Expand Down
10 changes: 5 additions & 5 deletions compiler/crates/graphql-ir/src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
* LICENSE file in the root directory of this source tree.
*/

use crate::error_combinators::{try2, try3, try_all};
use crate::errors::{ValidationError, ValidationMessage, ValidationResult};
use crate::ir::*;
use crate::signatures::{build_signatures, FragmentSignatures};
use common::{Location, Span, Spanned};
use errors::{try2, try3, try_map};
use fnv::{FnvHashMap, FnvHashSet};
use graphql_syntax::OperationKind;
use interner::Intern;
Expand All @@ -26,7 +26,7 @@ pub fn build_ir(
definitions: &[graphql_syntax::ExecutableDefinition],
) -> ValidationResult<Vec<ExecutableDefinition>> {
let signatures = build_signatures(schema, &definitions)?;
try_all(definitions, |definition| {
try_map(definitions, |definition| {
let mut builder = Builder::new(schema, &signatures, definition.location());
builder.build_definition(definition)
})
Expand Down Expand Up @@ -235,7 +235,7 @@ impl<'schema, 'signatures> Builder<'schema, 'signatures> {
&mut self,
definitions: &[graphql_syntax::VariableDefinition],
) -> ValidationResult<Vec<VariableDefinition>> {
try_all(definitions, |definition| {
try_map(definitions, |definition| {
self.build_variable_definition(definition)
})
}
Expand Down Expand Up @@ -313,7 +313,7 @@ impl<'schema, 'signatures> Builder<'schema, 'signatures> {
selections: &[graphql_syntax::Selection],
parent_type: &TypeReference,
) -> ValidationResult<Vec<Selection>> {
try_all(selections, |selection| {
try_map(selections, |selection| {
self.build_selection(selection, parent_type)
})
}
Expand Down Expand Up @@ -726,7 +726,7 @@ impl<'schema, 'signatures> Builder<'schema, 'signatures> {
directives: impl IntoIterator<Item = &'a graphql_syntax::Directive>,
location: DirectiveLocation,
) -> ValidationResult<Vec<Directive>> {
try_all(directives, |directive| {
try_map(directives, |directive| {
self.build_directive(directive, location)
})
}
Expand Down
Loading

0 comments on commit 5d733bc

Please sign in to comment.