GraphQL: Recursively call the resolve function to piece together the result of the tree structure.
struct Database {
products: Vec<Product>,
}
struct Query {
categories: Vec<Category>,
}
impl juniper::GraphQLType for Query {
fn name(_info: &Self::TypeInfo) -> Option<&str> {
Some("Query")
}
fn meta<'r>(
info: &Self::TypeInfo,
registry: &mut juniper::Registry<'r, juniper::DefaultScalarValue>,
) -> juniper::meta::MetaType<'r, juniper::DefaultScalarValue>
where
juniper::DefaultScalarValue: 'r,
{
let fields = &[
registry.field::<String>("hello", info),
registry.field::<Vec<Product>>("products", info),
registry
.field::<Option<Product>>("product", info)
.argument(registry.arg::<String>("name", info)),
registry.field::<Vec<Category>>("categories", info),
registry
.field::<Option<Category>>("category", info)
.argument(registry.arg::<String>("name", info)),
];
registry
.build_object_type::<Query>(info, fields)
.into_meta()
}
}
impl juniper::GraphQLValue for Query {
type Context = Database;
type TypeInfo = ();
fn type_name<'i>(&self, info: &'i Self::TypeInfo) -> Option<&'i str> {
<Self as juniper::GraphQLType>::name(info)
}
fn resolve_field(
&self,
info: &Self::TypeInfo,
field_name: &str,
arguments: &juniper::Arguments<juniper::DefaultScalarValue>,
executor: &juniper::Executor<Self::Context, juniper::DefaultScalarValue>,
) -> juniper::ExecutionResult<juniper::DefaultScalarValue> {
match field_name {
"products" => executor.resolve_with_ctx(info, &executor.context().products),
"product" => {
let name = arguments.get::<String>("name")?.unwrap();
let product = executor.context().products.iter().find(|p| p.name == name);
executor.resolve_with_ctx(info, &product)
}
"categories" => executor.resolve(info, &self.categories),
"category" => {
let name = arguments.get::<String>("name")?.unwrap();
let category = self.categories.iter().find(|c| c.name == name);
executor.resolve(info, &category)
}
_ => panic!(),
}
}
}
struct Product {
name: String,
price: f64,
category_name: String,
}
impl juniper::GraphQLType for Product {
fn name(_info: &Self::TypeInfo) -> Option<&str> {
Some("Product")
}
fn meta<'r>(
info: &Self::TypeInfo,
registry: &mut juniper::Registry<'r, juniper::DefaultScalarValue>,
) -> juniper::meta::MetaType<'r, juniper::DefaultScalarValue>
where
juniper::DefaultScalarValue: 'r,
{
let fields = &[
registry.field::<String>("name", info),
registry.field::<f64>("price", info),
registry.field::<String>("category_name", info),
];
registry
.build_object_type::<Product>(info, fields)
.into_meta()
}
}
impl juniper::GraphQLValue for Product {
type Context = ();
type TypeInfo = ();
fn type_name<'i>(&self, _info: &'i Self::TypeInfo) -> Option<&'i str> {
Some("Product")
}
fn resolve_field(
&self,
info: &Self::TypeInfo,
field_name: &str,
_arguments: &juniper::Arguments<juniper::DefaultScalarValue>,
executor: &juniper::Executor<Self::Context, juniper::DefaultScalarValue>,
) -> juniper::ExecutionResult<juniper::DefaultScalarValue> {
match field_name {
"name" => executor.resolve_with_ctx(info, &self.name),
"price" => executor.resolve_with_ctx(info, &self.price),
"category_name" => executor.resolve_with_ctx(info, &self.category_name),
_ => panic!(),
}
}
}
struct Category {
name: String,
}
impl juniper::GraphQLType for Category {
fn name(_info: &Self::TypeInfo) -> Option<&str> {
Some("Category")
}
fn meta<'r>(
info: &Self::TypeInfo,
registry: &mut juniper::Registry<'r, juniper::DefaultScalarValue>,
) -> juniper::meta::MetaType<'r, juniper::DefaultScalarValue>
where
juniper::DefaultScalarValue: 'r,
{
let fields = &[
registry.field::<String>("name", info),
registry.field::<Vec<Product>>("products", info),
];
registry
.build_object_type::<Category>(info, fields)
.into_meta()
}
}
impl juniper::GraphQLValue for Category {
type Context = Database;
type TypeInfo = ();
fn type_name<'i>(&self, _info: &'i Self::TypeInfo) -> Option<&'i str> {
Some("Category")
}
fn resolve_field(
&self,
info: &Self::TypeInfo,
field_name: &str,
_arguments: &juniper::Arguments<juniper::DefaultScalarValue>,
executor: &juniper::Executor<Self::Context, juniper::DefaultScalarValue>,
) -> juniper::ExecutionResult<juniper::DefaultScalarValue> {
match field_name {
"name" => executor.resolve_with_ctx(info, &self.name),
"products" => {
let products = executor
.context()
.products
.iter()
.filter(|p| p.category_name == self.name)
.collect::<Vec<_>>();
executor.resolve_with_ctx(info, &products)
}
_ => panic!(),
}
}
}