Skip to content

Commit

Permalink
chore: add variable support
Browse files Browse the repository at this point in the history
  • Loading branch information
erikwrede committed May 21, 2024
1 parent 423b533 commit e02219a
Show file tree
Hide file tree
Showing 2 changed files with 165 additions and 30 deletions.
171 changes: 141 additions & 30 deletions src/ast/gql_core/converter.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
use std::ops::Deref;

use apollo_compiler::{ExecutableDocument, Node};
use apollo_compiler::ast::{Argument, Value};
use apollo_compiler::executable::{Field, OperationType, Selection, SelectionSet};
use pyo3::{PyAny, Python};
use pyo3::prelude::*;
use pyo3::types::{PyDict, PyList, PyString};
use pyo3::types::{PyDict, PyTuple, PyString};
use crate::ast::gql_core::reduced_core_mirror::ArgumentNode;

struct CoreOperationType {
Query: Py<PyAny>,
Expand Down Expand Up @@ -41,6 +45,19 @@ pub struct CoreConversionContext {
field_node: Py<PyAny>,
document_node: Py<PyAny>,
name_node: Py<PyAny>,
argument_node: Py<PyAny>,

value_node: Py<PyAny>,
variable_node: Py<PyAny>,
int_value_node: Py<PyAny>,
float_value_node: Py<PyAny>,
string_value_node: Py<PyAny>,
boolean_value_node: Py<PyAny>,
null_value_node: Py<PyAny>,
enum_value_node: Py<PyAny>,
list_value_node: Py<PyAny>,
object_value_node: Py<PyAny>,
object_field_node: Py<PyAny>,

}

Expand All @@ -53,6 +70,20 @@ impl CoreConversionContext {
let PySelectionSetNode = graphql_core_ast.getattr("SelectionSetNode").unwrap();
let PyFieldNode = graphql_core_ast.getattr("FieldNode").unwrap();
let PyNameNode = graphql_core_ast.getattr("NameNode").unwrap();
let PyArgumentNode = graphql_core_ast.getattr("ArgumentNode").unwrap();

let PyValueNode = graphql_core_ast.getattr("ValueNode").unwrap();
let PyVariableNode = graphql_core_ast.getattr("VariableNode").unwrap();
let PyIntValueNode = graphql_core_ast.getattr("IntValueNode").unwrap();
let PyFloatValueNode = graphql_core_ast.getattr("FloatValueNode").unwrap();
let PyStringValueNode = graphql_core_ast.getattr("StringValueNode").unwrap();
let PyBooleanValueNode = graphql_core_ast.getattr("BooleanValueNode").unwrap();
let PyNullValueNode = graphql_core_ast.getattr("NullValueNode").unwrap();
let PyEnumValueNode = graphql_core_ast.getattr("EnumValueNode").unwrap();
let PyTupleValueNode = graphql_core_ast.getattr("ListValueNode").unwrap();
let PyObjectValueNode = graphql_core_ast.getattr("ObjectValueNode").unwrap();
let PyObjectFieldNode = graphql_core_ast.getattr("ObjectFieldNode").unwrap();


Self {
operation_type: CoreOperationType::new(PyOperationType),
Expand All @@ -61,6 +92,19 @@ impl CoreConversionContext {
field_node: PyFieldNode.into(),
document_node: PyDocumentNode.into(),
name_node: PyNameNode.into(),
argument_node: PyArgumentNode.into(),

value_node: PyValueNode.into(),
variable_node: PyVariableNode.into(),
int_value_node: PyIntValueNode.into(),
float_value_node: PyFloatValueNode.into(),
string_value_node: PyStringValueNode.into(),
boolean_value_node: PyBooleanValueNode.into(),
null_value_node: PyNullValueNode.into(),
enum_value_node: PyEnumValueNode.into(),
list_value_node: PyTupleValueNode.into(),
object_value_node: PyObjectValueNode.into(),
object_field_node: PyObjectFieldNode.into(),
}
}

Expand All @@ -72,6 +116,70 @@ impl CoreConversionContext {

self.name_node.call_bound(py, (), Some(&name_node_kwargs))
}
fn convert_value_to_core_value(&self, py: Python, value: &Node<Value>) -> PyResult<PyObject> {
let value_node_kwargs = PyDict::new_bound(py);

match value.deref() {
Value::Null => {
self.null_value_node.call_bound(py, (), None)
}
Value::Enum(name) => {
value_node_kwargs.set_item("value", name.as_str())?;
self.enum_value_node.call_bound(py, (), Some(&value_node_kwargs))
}
Value::Variable(name) => {
value_node_kwargs.set_item("name", self.get_name_node(py, name.as_str())?)?;
self.variable_node.call_bound(py, (), Some(&value_node_kwargs))
}
Value::String(string) => {
value_node_kwargs.set_item("value", string.as_str())?;
self.string_value_node.call_bound(py, (), Some(&value_node_kwargs))
}
Value::Float(float) => {
value_node_kwargs.set_item("value", float.to_string())?;
self.float_value_node.call_bound(py, (), Some(&value_node_kwargs))
}
Value::Int(int) => {
value_node_kwargs.set_item("value", int.to_string())?;
self.int_value_node.call_bound(py, (), Some(&value_node_kwargs))
}
Value::Boolean(boolean) => {
value_node_kwargs.set_item("value", boolean)?;
self.boolean_value_node.call_bound(py, (), Some(&value_node_kwargs))
}
Value::List(values) => {
let core_values: Vec<PyObject> = values.iter().map(|value| {
self.convert_value_to_core_value(py, value)
}).collect::<PyResult<_>>()?;
value_node_kwargs.set_item("values", PyTuple::new_bound(py, core_values.iter()))?;
self.list_value_node.call_bound(py, (), Some(&value_node_kwargs))
}
Value::Object(fields) => {
let core_fields: Vec<PyObject> = fields.iter().map(|(name, value)| {
let object_field_node_kwargs = PyDict::new_bound(py);
object_field_node_kwargs.set_item("name", self.get_name_node(py, name.as_str())?)?;
object_field_node_kwargs.set_item("value", self.convert_value_to_core_value(py, value)?)?;

self.object_field_node.call_bound(py, (), Some(&object_field_node_kwargs))
}).collect::<PyResult<_>>()?;

value_node_kwargs.set_item("fields", PyTuple::new_bound(py, core_fields.iter()))?;
self.object_value_node.call_bound(py, (), Some(&value_node_kwargs))
}
}
}


fn convert_argument_to_core_argument(&self, py: Python, argument: &Node<Argument>) -> PyResult<PyObject> {
let name = self.get_name_node(py, argument.name.as_str());
let value = self.convert_value_to_core_value(py, &argument.value);


let argument_node_kwargs = PyDict::new_bound(py);
argument_node_kwargs.set_item("name", name?)?;
argument_node_kwargs.set_item("value", value?)?;
self.argument_node.call_bound(py, (), Some(&argument_node_kwargs))
}

fn convert_field_to_core_field(&self, py: Python, field: &Node<Field>) -> PyResult<PyObject> {
//println!("Converting field to core field...");
Expand All @@ -85,7 +193,7 @@ impl CoreConversionContext {

//println!("Alias");
if let Some(alias) = &field.alias {
field_node_kwargs.set_item("alias", PyString::new_bound(py, alias.as_str()))?;
field_node_kwargs.set_item("alias", self.get_name_node(py, alias.as_str())?)?;
}

//println!("Name");
Expand All @@ -95,8 +203,12 @@ impl CoreConversionContext {
//println!("Initing lists");

// init an empty list of pyobjects
let arguments = PyList::empty_bound(py).to_object(py);
let directives = PyList::empty_bound(py).to_object(py);
let arguments : Vec<PyObject> = field.arguments.iter().map(|argument| {
self.convert_argument_to_core_argument(py, argument)
}).collect::<PyResult<_>>()?;

let arguments = PyTuple::new_bound(py, arguments.iter());
let directives = PyTuple::empty_bound(py).to_object(py);


field_node_kwargs.set_item("arguments", arguments)?;
Expand All @@ -112,19 +224,26 @@ impl CoreConversionContext {
//println!("Converting selection set...");
let selection_set_kwargs = PyDict::new_bound(py);
// FIXME do we NEED to use PyTuple here?
let selections = PyList::empty_bound(py);

for selection in &selection_set.selections {
let core_selection = match selection {
Selection::Field(field) => Some(self.convert_field_to_core_field(py, field)),
Selection::FragmentSpread(fragment_spread) => None,//self.convert_fragment_spread_to_core_fragment_spread(py, fragment_spread),
Selection::InlineFragment(inline_fragment) => None,// self.convert_inline_fragment_to_core_inline_fragment(py, inline_fragment),
};
if let Some(core_selection) = core_selection {
//println!("Appending new Selection to the set...");
selections.append(core_selection?)?;


let selection_vec : Vec<PyObject> = selection_set.selections.iter().map(|selection| {
match selection {
Selection::Field(field) => self.convert_field_to_core_field(py, field),
Selection::FragmentSpread(_) => {
return Err(PyErr::new::<pyo3::exceptions::PyNotImplementedError, _>(
"FragmentSpread is not yet implemented",
))
}
Selection::InlineFragment(_) => {
return Err(PyErr::new::<pyo3::exceptions::PyNotImplementedError, _>(
"InlineFragment is not yet implemented",
))
}
}
}
}).collect::<PyResult<_>>()?;

let selections = PyTuple::new_bound(py, selection_vec.iter());

//println!("Done converting selections!");
selection_set_kwargs.set_item("selections", selections)?;
//println!("Appended selections to kwargs!");
Expand All @@ -135,20 +254,15 @@ impl CoreConversionContext {
let operations = document.all_operations();
let fragments = &document.fragments;

let core_operations = PyList::empty_bound(py);

for operation in operations {
let core_operations : Vec<PyObject> = operations.map(|operation| {
let operation_kwargs = PyDict::new_bound(py);

if let Some(operation_name) = &operation.name{
if let Some(operation_name) = &operation.name {
let operation_name = self.get_name_node(py, operation_name)?;
// FIXME is this necessary?
//println!("Trying to set name!");
operation_kwargs.set_item("name", operation_name)?;
//println!("Name set!");
}


let operation_type = self.operation_type.get_operation_type(operation.operation_type);

//println!("Operation type resolved!");
Expand All @@ -167,15 +281,12 @@ impl CoreConversionContext {

//println!("Operation type set kwarg!");
operation_kwargs.set_item("selection_set", self.convert_selection_set_to_core_selection_set(py, selection_set)?)?;
//println!("Selection Set converted!");

//println!("Creating Operation def node...");
core_operations.append(self.operation_definition.call_bound(py, (), Some(&operation_kwargs))?)?;
self.operation_definition.call_bound(py, (), Some(&operation_kwargs))
}).collect::<PyResult<Vec<PyObject>>>()?;

//println!("Created Operation def node!");
}
let document_node_kwargs = PyDict::new_bound(py);
document_node_kwargs.set_item("definitions", core_operations)?;
let py_operations = PyTuple::new_bound(py, core_operations.iter());
document_node_kwargs.set_item("definitions", py_operations)?;

//println!("Creating document node!");
self.document_node.call_bound(py, (), Some(&document_node_kwargs))
Expand Down
24 changes: 24 additions & 0 deletions test_parser/benchmarks/test_core_full_schema_bench.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import pytest
from graphql import execute, GraphQLObjectType, GraphQLArgument, GraphQLSchema, GraphQLField, GraphQLString, \
GraphQLList, validate, ExecutionResult
from graphql.language.printer import print_ast

from rustberry import QueryCompiler

Expand Down Expand Up @@ -273,6 +274,17 @@ def test_pycon_query_execution_rustberry():
e = execute(
schema, query,
)
@pytest.mark.benchmark
def test_pycon_query_execution_rustberry_no_mirror():
document = compiler.parse(operation)
validation_success = compiler.validate(document)
query = compiler.gql_core_ast_mirror(document)
if not validation_success:
return ExecutionResult(data=None, errors=validation_errors)

e = execute(
schema, query,
)

@pytest.mark.benchmark
def test_pure_execution_core(benchmark):
Expand All @@ -284,6 +296,11 @@ def test_pure_execution_rustberry(benchmark):
document = compiler.parse(operation)
query = compiler.gql_core_ast_mirror(document)
benchmark(execute, schema, query)
@pytest.mark.benchmark
def test_pure_execution_rustberry_no_mirror(benchmark):
document = compiler.parse(operation)
query = compiler.gql_core_ast(document)
benchmark(execute, schema, query)



Expand All @@ -297,3 +314,10 @@ def test_pure_execution_rustberry_large(benchmark):
document = compiler.parse(operation_large)
query = compiler.gql_core_ast_mirror(document)
benchmark(execute, schema, query)

@pytest.mark.benchmark
def test_pure_execution_rustberry_large_no_mirrpr(benchmark):
document = compiler.parse(operation_large)
query = compiler.gql_core_ast(document)
#print(print_ast(query))
benchmark(execute, schema, query)

0 comments on commit e02219a

Please sign in to comment.