use std::{
collections::{BTreeMap, BTreeSet},
convert::TryFrom,
ptr,
sync::Arc,
};
use async_graphql_parser::types::{BaseType, Type};
use serde::{Deserialize, Serialize};
use crate::util::BTreeMapTryInsertExt;
use super::{
types::is_scalar_only_subtype, Argument, Eid, IREdge, IRFold, IRQuery, IRQueryComponent, Vid,
};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct IndexedQuery {
pub ir_query: IRQuery,
pub vids: BTreeMap<Vid, Arc<IRQueryComponent>>,
pub eids: BTreeMap<Eid, EdgeKind>,
pub outputs: BTreeMap<Arc<str>, Output>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Output {
pub name: Arc<str>,
#[serde(serialize_with = "crate::ir::serialization::serde_type_serializer")]
#[serde(deserialize_with = "crate::ir::serialization::serde_type_deserializer")]
pub value_type: Type,
pub vid: Vid,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum InvalidIRQueryError {
GetBetterVariant(i32),
}
impl TryFrom<IRQuery> for IndexedQuery {
type Error = InvalidIRQueryError;
fn try_from(ir_query: IRQuery) -> Result<Self, Self::Error> {
let mut vids = Default::default();
let mut eids = Default::default();
let mut outputs = Default::default();
add_data_from_component(
&mut vids,
&mut eids,
&mut outputs,
&ir_query.variables,
&ir_query.root_component,
&mut vec![],
)?;
Ok(Self {
ir_query,
vids,
eids,
outputs,
})
}
}
fn get_optional_vertices_in_component(component: &Arc<IRQueryComponent>) -> BTreeSet<Vid> {
let mut output = BTreeSet::new();
for edge in component.edges.values() {
if edge.optional || output.contains(&edge.from_vid) {
output.insert(edge.to_vid);
}
}
output
}
fn get_output_type(
output_at: Vid,
field_type: &Type,
component_optional_vertices: &BTreeSet<Vid>,
are_folds_optional: &[bool],
) -> Type {
let mut wrapped_output_type = field_type.clone();
if component_optional_vertices.contains(&output_at) {
wrapped_output_type.nullable = true;
}
for is_fold_optional in are_folds_optional.iter().rev() {
wrapped_output_type = Type {
base: BaseType::List(Box::new(wrapped_output_type)),
nullable: *is_fold_optional,
};
}
wrapped_output_type
}
fn add_data_from_component(
vids: &mut BTreeMap<Vid, Arc<IRQueryComponent>>,
eids: &mut BTreeMap<Eid, EdgeKind>,
outputs: &mut BTreeMap<Arc<str>, Output>,
variables: &BTreeMap<Arc<str>, Type>,
component: &Arc<IRQueryComponent>,
are_folds_optional: &mut Vec<bool>, ) -> Result<(), InvalidIRQueryError> {
let component_optional_vertices = get_optional_vertices_in_component(component);
if component.vertices.get(&component.root).is_none() {
return Err(InvalidIRQueryError::GetBetterVariant(-1));
}
for (vid, vertex) in &component.vertices {
let existing = vids.insert(*vid, component.clone());
if existing.is_some() {
return Err(InvalidIRQueryError::GetBetterVariant(0));
}
for filter in &vertex.filters {
match filter.right() {
Some(Argument::Variable(vref)) => {
match variables.get(&vref.variable_name) {
Some(var_type) => {
if !is_scalar_only_subtype(&vref.variable_type, var_type) {
return Err(InvalidIRQueryError::GetBetterVariant(-2));
}
}
None => {
return Err(InvalidIRQueryError::GetBetterVariant(-3));
}
}
}
Some(Argument::Tag(..)) | None => {}
}
}
}
for (output_name, field) in component.outputs.iter() {
let output_vid = field.vertex_id;
let output_component = vids
.get(&output_vid)
.ok_or(InvalidIRQueryError::GetBetterVariant(1))?;
if !ptr::eq(component.as_ref(), output_component.as_ref()) {
return Err(InvalidIRQueryError::GetBetterVariant(2));
}
let output_name = output_name.clone();
let output_type = get_output_type(
output_vid,
&field.field_type,
&component_optional_vertices,
are_folds_optional,
);
let output = Output {
name: output_name.clone(),
value_type: output_type,
vid: output_vid,
};
let existing = outputs.insert(output_name, output);
if existing.is_some() {
return Err(InvalidIRQueryError::GetBetterVariant(3));
}
}
for (eid, edge) in component.edges.iter() {
if usize::from(eid.0) + 1 != usize::from(edge.to_vid.0) {
return Err(InvalidIRQueryError::GetBetterVariant(4));
}
let from_component = vids
.get(&edge.from_vid)
.ok_or(InvalidIRQueryError::GetBetterVariant(5))?;
if !ptr::eq(component.as_ref(), from_component.as_ref()) {
return Err(InvalidIRQueryError::GetBetterVariant(6));
}
let to_component = vids
.get(&edge.to_vid)
.ok_or(InvalidIRQueryError::GetBetterVariant(7))?;
if !ptr::eq(component.as_ref(), to_component.as_ref()) {
return Err(InvalidIRQueryError::GetBetterVariant(8));
}
let existing = eids.insert(*eid, EdgeKind::Regular(edge.clone()));
if existing.is_some() {
return Err(InvalidIRQueryError::GetBetterVariant(9));
}
}
for (eid, fold) in component.folds.iter() {
if usize::from(eid.0) + 1 != usize::from(fold.to_vid.0) {
return Err(InvalidIRQueryError::GetBetterVariant(10));
}
let from_component = vids
.get(&fold.from_vid)
.ok_or(InvalidIRQueryError::GetBetterVariant(11))?;
if !ptr::eq(component.as_ref(), from_component.as_ref()) {
return Err(InvalidIRQueryError::GetBetterVariant(12));
}
if fold.to_vid != fold.component.root {
return Err(InvalidIRQueryError::GetBetterVariant(13));
}
let existing = eids.insert(*eid, EdgeKind::Fold(fold.clone()));
if existing.is_some() {
return Err(InvalidIRQueryError::GetBetterVariant(14));
}
for (name, kind) in &fold.fold_specific_outputs {
let output_type = get_output_type(
fold.from_vid,
kind.field_type(),
&component_optional_vertices,
are_folds_optional,
);
outputs
.insert_or_error(
name.clone(),
Output {
name: name.clone(),
value_type: output_type,
vid: fold.to_vid,
},
)
.map_err(|_| InvalidIRQueryError::GetBetterVariant(15))?;
}
are_folds_optional.push(component_optional_vertices.contains(&fold.from_vid));
add_data_from_component(
vids,
eids,
outputs,
variables,
&fold.component,
are_folds_optional,
)?;
are_folds_optional
.pop()
.expect("pushed value is no longer present");
}
Ok(())
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum EdgeKind {
Regular(Arc<IREdge>),
Fold(Arc<IRFold>),
}
impl From<Arc<IREdge>> for EdgeKind {
fn from(edge: Arc<IREdge>) -> Self {
Self::Regular(edge)
}
}
impl From<Arc<IRFold>> for EdgeKind {
fn from(fold: Arc<IRFold>) -> Self {
Self::Fold(fold)
}
}