1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
use crate::{
    do_resolve, registry, Context, ContextSelectionSet, ObjectType, OutputValueType, Result, Type,
};
use graphql_parser::query::Field;
use graphql_parser::Pos;
use std::borrow::Cow;
use std::collections::HashMap;

pub struct Edge<'a, T, E> {
    pub cursor: &'a str,
    pub node: &'a T,
    pub extra_type: &'a E,
}

impl<'a, T, E> Type for Edge<'a, T, E>
where
    T: OutputValueType + Send + Sync + 'a,
    E: ObjectType + Sync + Send + 'a,
{
    fn type_name() -> Cow<'static, str> {
        Cow::Owned(format!("{}Edge", T::type_name()))
    }

    fn create_type_info(registry: &mut registry::Registry) -> String {
        registry.create_type::<Self, _>(|registry| {
            E::create_type_info(registry);
            let extra_fields = if let Some(registry::Type::Object { fields, .. }) =
                registry.types.get_mut(E::type_name().as_ref())
            {
                fields.clone()
            } else {
                unreachable!()
            };
            registry.types.remove(E::type_name().as_ref());

            registry::Type::Object {
                name: Self::type_name().to_string(),
                description: Some("An edge in a connection."),
                fields: {
                    let mut fields = HashMap::new();

                    fields.insert(
                        "node".to_string(),
                        registry::Field {
                            name: "node".to_string(),
                            description: Some("The item at the end of the edge"),
                            args: Default::default(),
                            ty: T::create_type_info(registry),
                            deprecation: None,
                            cache_control: Default::default(),
                            external: false,
                            requires: None,
                            provides: None,
                        },
                    );

                    fields.insert(
                        "cursor".to_string(),
                        registry::Field {
                            name: "cursor".to_string(),
                            description: Some("A cursor for use in pagination"),
                            args: Default::default(),
                            ty: String::create_type_info(registry),
                            deprecation: None,
                            cache_control: Default::default(),
                            external: false,
                            requires: None,
                            provides: None,
                        },
                    );

                    fields.extend(extra_fields);
                    fields
                },
                cache_control: Default::default(),
                extends: false,
                keys: None,
            }
        })
    }
}

#[async_trait::async_trait]
impl<'a, T, E> ObjectType for Edge<'a, T, E>
where
    T: OutputValueType + Send + Sync + 'a,
    E: ObjectType + Sync + Send + 'a,
{
    async fn resolve_field(&self, ctx: &Context<'_>, field: &Field) -> Result<serde_json::Value> {
        if field.name.as_str() == "node" {
            let ctx_obj = ctx.with_selection_set(&field.selection_set);
            return OutputValueType::resolve(self.node, &ctx_obj, field.position).await;
        } else if field.name.as_str() == "cursor" {
            return Ok(self.cursor.into());
        }

        self.extra_type.resolve_field(ctx, field).await
    }
}

#[async_trait::async_trait]
impl<'a, T, E> OutputValueType for Edge<'a, T, E>
where
    T: OutputValueType + Send + Sync + 'a,
    E: ObjectType + Sync + Send + 'a,
{
    async fn resolve(
        value: &Self,
        ctx: &ContextSelectionSet<'_>,
        _pos: Pos,
    ) -> Result<serde_json::Value> {
        do_resolve(ctx, value).await
    }
}