use timely::dataflow::scopes::child::Iterative;
use timely::dataflow::Scope;
use timely::progress::Timestamp;
use differential_dataflow::lattice::Lattice;
use graphql_parser::parse_query;
use graphql_parser::query::{Definition, Document, OperationDefinition, Selection, SelectionSet};
use graphql_parser::query::{Name, Value};
use crate::binding::Binding;
use crate::plan::{gensym, Dependencies, ImplContext, Implementable};
use crate::plan::{Hector, Plan, Pull, PullAll, PullLevel};
use crate::{Aid, Var};
use crate::{Implemented, ShutdownHandle, VariableMap};
#[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Debug, Serialize, Deserialize)]
pub struct GraphQl {
pub query: String,
paths: Vec<Plan>,
}
impl GraphQl {
pub fn new(query: String) -> Self {
let ast = parse_query(&query).expect("graphQL ast parsing failed");
let empty_plan = Hector {
variables: vec![0],
bindings: vec![],
};
GraphQl {
query,
paths: ast.into_paths(empty_plan),
}
}
pub fn with_plan(root_plan: Plan, query: String) -> Self {
let ast = parse_query(&query).expect("graphQL ast parsing failed");
let paths = ast.into_paths(Hector {
variables: root_plan.variables(),
bindings: root_plan.into_bindings(),
});
GraphQl { query, paths }
}
}
trait IntoPaths {
fn into_paths(&self, root_plan: Hector) -> Vec<Plan>;
}
impl IntoPaths for Document {
fn into_paths(&self, root_plan: Hector) -> Vec<Plan> {
self.definitions
.iter()
.flat_map(|definition| definition.into_paths(root_plan.clone()))
.collect()
}
}
impl IntoPaths for Definition {
fn into_paths(&self, root_plan: Hector) -> Vec<Plan> {
match self {
Definition::Operation(operation) => operation.into_paths(root_plan),
Definition::Fragment(_) => unimplemented!(),
}
}
}
impl IntoPaths for OperationDefinition {
fn into_paths(&self, root_plan: Hector) -> Vec<Plan> {
use OperationDefinition::{Query, SelectionSet};
match self {
Query(_) => unimplemented!(),
SelectionSet(selection_set) => {
selection_set_to_paths(&selection_set, root_plan, &[], &[])
}
_ => unimplemented!(),
}
}
}
fn pull_attributes(selection_set: &SelectionSet) -> Vec<Aid> {
selection_set
.items
.iter()
.flat_map(|item| match item {
Selection::Field(field) => {
if field.selection_set.items.is_empty() {
Some(field.name.to_string())
} else {
None
}
}
_ => unimplemented!(),
})
.collect::<Vec<Aid>>()
}
fn selection_set_to_paths(
selection_set: &SelectionSet,
mut plan: Hector,
arguments: &[(Name, Value)],
parent_path: &[String],
) -> Vec<Plan> {
if !parent_path.is_empty() {
let parent = *plan.variables.last().unwrap();
let this = plan.variables.len() as Var;
let aid = parent_path.last().unwrap();
plan.variables.push(this);
plan.bindings.push(Binding::attribute(parent, aid, this));
}
let this = *plan.variables.last().unwrap();
for (aid, v) in arguments.iter() {
let vsym = gensym();
plan.bindings.push(Binding::attribute(this, aid, vsym));
plan.bindings
.push(Binding::constant(vsym, v.clone().into()));
}
let pull_attributes = pull_attributes(selection_set);
let nested_levels = selection_set
.items
.iter()
.flat_map(|item| match item {
Selection::Field(field) => {
if !field.selection_set.items.is_empty() {
let mut parent_path = parent_path.to_vec();
parent_path.push(field.name.to_string());
selection_set_to_paths(
&field.selection_set,
plan.clone(),
&field.arguments,
&parent_path,
)
} else {
vec![]
}
}
_ => unimplemented!(),
})
.collect::<Vec<Plan>>();
let mut levels = nested_levels;
if !pull_attributes.is_empty() {
if plan.bindings.is_empty() {
levels.push(Plan::PullAll(PullAll {
variables: vec![],
pull_attributes,
}));
} else {
levels.push(Plan::PullLevel(PullLevel {
pull_attributes,
path_attributes: parent_path.to_vec(),
pull_variable: this,
variables: vec![],
plan: Box::new(Plan::Hector(plan)),
cardinality_many: false,
}));
}
}
levels
}
impl Implementable for GraphQl {
fn dependencies(&self) -> Dependencies {
let mut dependencies = Dependencies::none();
for path in self.paths.iter() {
dependencies = Dependencies::merge(dependencies, path.dependencies());
}
dependencies
}
fn implement<'b, T, I, S>(
&self,
nested: &mut Iterative<'b, S, u64>,
local_arrangements: &VariableMap<Iterative<'b, S, u64>>,
context: &mut I,
) -> (Implemented<'b, S>, ShutdownHandle)
where
T: Timestamp + Lattice,
I: ImplContext<T>,
S: Scope<Timestamp = T>,
{
let parsed = Pull {
variables: vec![],
paths: self.paths.clone(),
};
parsed.implement(nested, local_arrangements, context)
}
}
impl std::convert::From<Value> for crate::Value {
fn from(v: Value) -> crate::Value {
match v {
Value::Int(v) => crate::Value::Number(v.as_i64().expect("failed to convert to i64")),
Value::String(v) => crate::Value::String(v),
Value::Boolean(v) => crate::Value::Bool(v),
_ => unimplemented!(),
}
}
}