use std::{collections::BTreeMap, fmt::Debug, sync::Arc};
use itertools::Itertools;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use crate::{
ir::{EdgeParameters, Eid, FieldRef, FieldValue, IndexedQuery, Type, Vid},
util::BTreeMapTryInsertExt,
};
use self::error::QueryArgumentsError;
pub mod basic_adapter;
pub mod error;
pub mod execution;
mod filtering;
pub mod helpers;
mod hints;
pub mod replay;
pub mod trace;
pub use hints::{
CandidateValue, DynamicallyResolvedValue, EdgeInfo, NeighborInfo, QueryInfo, Range,
RequiredProperty, ResolveEdgeInfo, ResolveInfo, VertexInfo,
};
pub type VertexIterator<'vertex, VertexT> = Box<dyn Iterator<Item = VertexT> + 'vertex>;
pub type ContextIterator<'vertex, VertexT> = VertexIterator<'vertex, DataContext<VertexT>>;
pub type ContextOutcomeIterator<'vertex, VertexT, OutcomeT> =
Box<dyn Iterator<Item = (DataContext<VertexT>, OutcomeT)> + 'vertex>;
pub trait Typename {
fn typename(&self) -> &'static str;
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) enum TaggedValue {
NonexistentOptional,
Some(FieldValue),
}
#[derive(Debug, Clone)]
pub struct DataContext<Vertex> {
active_vertex: Option<Vertex>,
vertices: BTreeMap<Vid, Option<Vertex>>,
values: Vec<FieldValue>,
suspended_vertices: Vec<Option<Vertex>>,
folded_contexts: BTreeMap<Eid, Option<Vec<DataContext<Vertex>>>>,
folded_values: BTreeMap<(Eid, Arc<str>), Option<ValueOrVec>>,
piggyback: Option<Vec<DataContext<Vertex>>>,
imported_tags: BTreeMap<FieldRef, TaggedValue>,
}
impl<Vertex> DataContext<Vertex> {
pub fn active_vertex<V>(&self) -> Option<&V>
where
Vertex: AsVertex<V>,
{
self.active_vertex.as_ref().and_then(AsVertex::as_vertex)
}
#[inline]
pub(crate) fn within_nonexistent_optional(&self) -> bool {
self.active_vertex.is_none()
}
pub fn map<Other>(self, mapper: &mut impl FnMut(Vertex) -> Other) -> DataContext<Other> {
DataContext {
active_vertex: self.active_vertex.map(&mut *mapper),
vertices: self.vertices.into_iter().map(|(k, v)| (k, v.map(&mut *mapper))).collect(),
values: self.values,
suspended_vertices: self
.suspended_vertices
.into_iter()
.map(|v| v.map(&mut *mapper))
.collect(),
folded_contexts: self
.folded_contexts
.into_iter()
.map(|(k, ctxs)| {
(k, ctxs.map(|v| v.into_iter().map(|ctx| ctx.map(&mut *mapper)).collect()))
})
.collect(),
folded_values: self.folded_values,
piggyback: self
.piggyback
.map(|v| v.into_iter().map(|ctx| ctx.map(&mut *mapper)).collect()),
imported_tags: self.imported_tags,
}
}
pub fn flat_map<T>(self, mapper: &mut impl FnMut(Vertex) -> Option<T>) -> DataContext<T> {
DataContext {
active_vertex: self.active_vertex.and_then(&mut *mapper),
vertices: self
.vertices
.into_iter()
.map(|(k, v)| (k, v.and_then(&mut *mapper)))
.collect::<BTreeMap<Vid, Option<T>>>(),
values: self.values,
suspended_vertices: self
.suspended_vertices
.into_iter()
.map(|v| v.and_then(&mut *mapper))
.collect(),
folded_contexts: self
.folded_contexts
.into_iter()
.map(|(k, ctxs)| {
(k, ctxs.map(|v| v.into_iter().map(|ctx| ctx.flat_map(&mut *mapper)).collect()))
})
.collect(),
folded_values: self.folded_values,
piggyback: self
.piggyback
.map(|v| v.into_iter().map(|ctx| ctx.flat_map(&mut *mapper)).collect()),
imported_tags: self.imported_tags,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
enum ValueOrVec {
Value(FieldValue),
Vec(Vec<ValueOrVec>),
}
impl ValueOrVec {
fn as_mut_vec(&mut self) -> Option<&mut Vec<ValueOrVec>> {
match self {
ValueOrVec::Value(_) => None,
ValueOrVec::Vec(v) => Some(v),
}
}
}
impl From<ValueOrVec> for FieldValue {
fn from(v: ValueOrVec) -> Self {
match v {
ValueOrVec::Value(value) => value,
ValueOrVec::Vec(v) => v.into(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(bound = "Vertex: Debug + Clone + Serialize + DeserializeOwned")]
struct SerializableContext<Vertex> {
active_vertex: Option<Vertex>,
vertices: BTreeMap<Vid, Option<Vertex>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
values: Vec<FieldValue>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
suspended_vertices: Vec<Option<Vertex>>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
folded_contexts: BTreeMap<Eid, Option<Vec<DataContext<Vertex>>>>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
folded_values: BTreeMap<(Eid, Arc<str>), Option<ValueOrVec>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
piggyback: Option<Vec<DataContext<Vertex>>>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
imported_tags: BTreeMap<FieldRef, TaggedValue>,
}
impl<Vertex> From<SerializableContext<Vertex>> for DataContext<Vertex> {
fn from(context: SerializableContext<Vertex>) -> Self {
Self {
active_vertex: context.active_vertex,
vertices: context.vertices,
values: context.values,
suspended_vertices: context.suspended_vertices,
folded_contexts: context.folded_contexts,
folded_values: context.folded_values,
piggyback: context.piggyback,
imported_tags: context.imported_tags,
}
}
}
impl<Vertex> From<DataContext<Vertex>> for SerializableContext<Vertex> {
fn from(context: DataContext<Vertex>) -> Self {
Self {
active_vertex: context.active_vertex,
vertices: context.vertices,
values: context.values,
suspended_vertices: context.suspended_vertices,
folded_contexts: context.folded_contexts,
folded_values: context.folded_values,
piggyback: context.piggyback,
imported_tags: context.imported_tags,
}
}
}
impl<Vertex: Clone + Debug> DataContext<Vertex> {
pub fn new(vertex: Option<Vertex>) -> DataContext<Vertex> {
DataContext {
active_vertex: vertex,
piggyback: None,
vertices: Default::default(),
values: Default::default(),
suspended_vertices: Default::default(),
folded_contexts: Default::default(),
folded_values: Default::default(),
imported_tags: Default::default(),
}
}
fn record_vertex(&mut self, vid: Vid) {
self.vertices.insert_or_error(vid, self.active_vertex.clone()).unwrap();
}
fn activate_vertex(self, vid: &Vid) -> DataContext<Vertex> {
DataContext {
active_vertex: self.vertices[vid].clone(),
vertices: self.vertices,
values: self.values,
suspended_vertices: self.suspended_vertices,
folded_contexts: self.folded_contexts,
folded_values: self.folded_values,
piggyback: self.piggyback,
imported_tags: self.imported_tags,
}
}
fn split_and_move_to_vertex(&self, new_vertex: Option<Vertex>) -> DataContext<Vertex> {
DataContext {
active_vertex: new_vertex,
vertices: self.vertices.clone(),
values: self.values.clone(),
suspended_vertices: self.suspended_vertices.clone(),
folded_contexts: self.folded_contexts.clone(),
folded_values: self.folded_values.clone(),
piggyback: None,
imported_tags: self.imported_tags.clone(),
}
}
fn move_to_vertex(self, new_vertex: Option<Vertex>) -> DataContext<Vertex> {
DataContext {
active_vertex: new_vertex,
vertices: self.vertices,
values: self.values,
suspended_vertices: self.suspended_vertices,
folded_contexts: self.folded_contexts,
folded_values: self.folded_values,
piggyback: self.piggyback,
imported_tags: self.imported_tags,
}
}
fn ensure_suspended(mut self) -> DataContext<Vertex> {
if let Some(vertex) = self.active_vertex {
self.suspended_vertices.push(Some(vertex));
DataContext {
active_vertex: None,
vertices: self.vertices,
values: self.values,
suspended_vertices: self.suspended_vertices,
folded_contexts: self.folded_contexts,
folded_values: self.folded_values,
piggyback: self.piggyback,
imported_tags: self.imported_tags,
}
} else {
self
}
}
fn ensure_unsuspended(mut self) -> DataContext<Vertex> {
match self.active_vertex {
None => {
let active_vertex = self.suspended_vertices.pop().unwrap();
DataContext {
active_vertex,
vertices: self.vertices,
values: self.values,
suspended_vertices: self.suspended_vertices,
folded_contexts: self.folded_contexts,
folded_values: self.folded_values,
piggyback: self.piggyback,
imported_tags: self.imported_tags,
}
}
Some(_) => self,
}
}
}
impl<Vertex: PartialEq> PartialEq for DataContext<Vertex> {
fn eq(&self, other: &Self) -> bool {
self.active_vertex == other.active_vertex
&& self.vertices == other.vertices
&& self.values == other.values
&& self.suspended_vertices == other.suspended_vertices
&& self.folded_contexts == other.folded_contexts
&& self.piggyback == other.piggyback
&& self.imported_tags == other.imported_tags
}
}
impl<Vertex: Eq> Eq for DataContext<Vertex> {}
impl<Vertex> Serialize for DataContext<Vertex>
where
Vertex: Debug + Clone + Serialize + DeserializeOwned,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
SerializableContext::from(self.clone()).serialize(serializer)
}
}
impl<'de, Vertex> Deserialize<'de> for DataContext<Vertex>
where
Vertex: Debug + Clone + Serialize + DeserializeOwned,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
SerializableContext::deserialize(deserializer).map(DataContext::from)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InterpretedQuery {
pub indexed_query: Arc<IndexedQuery>,
pub arguments: Arc<BTreeMap<Arc<str>, FieldValue>>,
}
impl InterpretedQuery {
#[inline]
pub fn from_query_and_arguments(
indexed_query: Arc<IndexedQuery>,
arguments: Arc<BTreeMap<Arc<str>, FieldValue>>,
) -> Result<Self, QueryArgumentsError> {
let mut errors = vec![];
let mut missing_arguments = vec![];
for (variable_name, variable_type) in &indexed_query.ir_query.variables {
match arguments.get(variable_name) {
Some(argument_value) => {
if let Err(e) = validate_argument_type(
variable_name.as_ref(),
variable_type,
argument_value,
) {
errors.push(e);
}
}
None => {
missing_arguments.push(variable_name.as_ref());
}
}
}
if !missing_arguments.is_empty() {
errors.push(QueryArgumentsError::MissingArguments(
missing_arguments.into_iter().map(|x| x.to_string()).collect(),
));
}
let unused_arguments = arguments
.keys()
.map(|x| x.as_ref())
.filter(|arg| !indexed_query.ir_query.variables.contains_key(*arg))
.collect_vec();
if !unused_arguments.is_empty() {
errors.push(QueryArgumentsError::UnusedArguments(
unused_arguments.into_iter().map(|x| x.to_string()).collect(),
));
}
if errors.is_empty() {
Ok(Self { indexed_query, arguments })
} else {
Err(errors.into())
}
}
}
fn validate_argument_type(
variable_name: &str,
variable_type: &Type,
argument_value: &FieldValue,
) -> Result<(), QueryArgumentsError> {
if variable_type.is_valid_value(argument_value) {
Ok(())
} else {
Err(QueryArgumentsError::ArgumentTypeError(
variable_name.to_string(),
variable_type.to_string(),
argument_value.to_owned(),
))
}
}
pub trait Adapter<'vertex> {
type Vertex: Clone + Debug + 'vertex;
fn resolve_starting_vertices(
&self,
edge_name: &Arc<str>,
parameters: &EdgeParameters,
resolve_info: &ResolveInfo,
) -> VertexIterator<'vertex, Self::Vertex>;
fn resolve_property<V: AsVertex<Self::Vertex> + 'vertex>(
&self,
contexts: ContextIterator<'vertex, V>,
type_name: &Arc<str>,
property_name: &Arc<str>,
resolve_info: &ResolveInfo,
) -> ContextOutcomeIterator<'vertex, V, FieldValue>;
fn resolve_neighbors<V: AsVertex<Self::Vertex> + 'vertex>(
&self,
contexts: ContextIterator<'vertex, V>,
type_name: &Arc<str>,
edge_name: &Arc<str>,
parameters: &EdgeParameters,
resolve_info: &ResolveEdgeInfo,
) -> ContextOutcomeIterator<'vertex, V, VertexIterator<'vertex, Self::Vertex>>;
fn resolve_coercion<V: AsVertex<Self::Vertex> + 'vertex>(
&self,
contexts: ContextIterator<'vertex, V>,
type_name: &Arc<str>,
coerce_to_type: &Arc<str>,
resolve_info: &ResolveInfo,
) -> ContextOutcomeIterator<'vertex, V, bool>;
}
pub trait AsVertex<V>: Debug + Clone {
fn as_vertex(&self) -> Option<&V>;
fn into_vertex(self) -> Option<V>;
}
pub trait Cast<V>: AsVertex<V> {
fn into_self(vertex: V) -> Self;
}
impl<V: Debug + Clone> AsVertex<V> for V {
fn as_vertex(&self) -> Option<&V> {
Some(self)
}
fn into_vertex(self) -> Option<V> {
Some(self)
}
}