use crate::execution::JsonMap;
use serde_json_bytes::Value as JsonValue;
pub(crate) type ObjectValue<'a> = dyn Resolver + 'a;
pub(crate) trait Resolver {
fn type_name(&self) -> &'static str;
fn resolve_field<'a>(
&'a self,
field_name: &'a str,
arguments: &'a JsonMap,
) -> Result<ResolvedValue<'_>, ResolverError>;
}
pub(crate) struct ResolverError {
pub(crate) message: String,
}
macro_rules! impl_resolver {
(
for $ty: ty:
__typename = $type_name: expr;
$(
fn $field_name: ident(
$( &$self_: ident $(, $( $args: ident $(,)? )? )? )?
) $block: block
)*
) => {
impl $crate::execution::resolver::Resolver for $ty {
fn type_name(&self) -> &'static str {
$type_name
}
fn resolve_field<'a>(
&'a self,
field_name: &'a str,
arguments: &'a $crate::execution::JsonMap,
) -> Result<
$crate::execution::resolver::ResolvedValue<'_>,
crate::execution::resolver::ResolverError
> {
let _allow_unused = arguments;
match field_name {
$(
stringify!($field_name) => {
$(
let $self_ = self;
$($(
let $args = arguments;
)?)?
)?
return $block
},
)*
_ => Err(crate::execution::resolver::ResolverError {
message: format!("unexpected field name: {field_name}")
}),
}
}
}
};
}
pub(crate) enum ResolvedValue<'a> {
Leaf(JsonValue),
Object(Box<ObjectValue<'a>>),
List(Box<dyn Iterator<Item = ResolvedValue<'a>> + 'a>),
}
impl<'a> ResolvedValue<'a> {
pub(crate) fn null() -> Self {
Self::Leaf(JsonValue::Null)
}
pub(crate) fn leaf(json: impl Into<JsonValue>) -> Self {
Self::Leaf(json.into())
}
pub(crate) fn object(resolver: impl Resolver + 'a) -> Self {
Self::Object(Box::new(resolver))
}
pub(crate) fn opt_object(opt_resolver: Option<impl Resolver + 'a>) -> Self {
match opt_resolver {
Some(resolver) => Self::Object(Box::new(resolver)),
None => Self::null(),
}
}
pub(crate) fn list<I>(iter: I) -> Self
where
I: IntoIterator<Item = Self>,
I::IntoIter: 'a,
{
Self::List(Box::new(iter.into_iter()))
}
}
#[cfg(test)]
mod tests {
use crate::execution::resolver::ResolvedValue;
struct QueryResolver {
world: String,
}
impl_resolver! {
for &'_ QueryResolver:
__typename = "Query";
fn null() {
Ok(ResolvedValue::null())
}
fn hello(&self_) {
Ok(ResolvedValue::list([
ResolvedValue::leaf(format!("Hello {}!", self_.world)),
ResolvedValue::leaf(format!("Hello {}!", self_.world)),
]))
}
fn echo(&_self, args) {
Ok(ResolvedValue::leaf(args["value"].clone()))
}
fn myself_again(&self_) {
Ok(ResolvedValue::object(*self_))
}
}
}