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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
use crate::{InputValueError, InputValueResult, Result, ScalarType, Value};
use async_graphql_derive::Scalar;
use bson::oid::{self, ObjectId};
use std::convert::TryInto;
use std::num::ParseIntError;
use std::ops::{Deref, DerefMut};
use uuid::Uuid;

/// ID scalar
///
/// The input is a `&str`, `String`, `usize` or `uuid::UUID`, and the output is a string.
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
pub struct ID(String);

impl std::fmt::Display for ID {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
}

impl Deref for ID {
    type Target = String;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl DerefMut for ID {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

impl From<String> for ID {
    fn from(value: String) -> Self {
        ID(value)
    }
}

impl Into<String> for ID {
    fn into(self) -> String {
        self.0
    }
}

impl<'a> From<&'a str> for ID {
    fn from(value: &'a str) -> Self {
        ID(value.to_string())
    }
}

impl From<usize> for ID {
    fn from(value: usize) -> Self {
        ID(value.to_string())
    }
}

impl TryInto<usize> for ID {
    type Error = ParseIntError;

    fn try_into(self) -> std::result::Result<usize, Self::Error> {
        self.0.parse()
    }
}

impl From<Uuid> for ID {
    fn from(uuid: Uuid) -> ID {
        ID(uuid.to_string())
    }
}

impl TryInto<Uuid> for ID {
    type Error = uuid::Error;

    fn try_into(self) -> std::result::Result<Uuid, Self::Error> {
        Uuid::parse_str(&self.0)
    }
}

impl From<ObjectId> for ID {
    fn from(object_id: ObjectId) -> ID {
        ID(object_id.to_hex())
    }
}

impl TryInto<ObjectId> for ID {
    type Error = oid::Error;

    fn try_into(self) -> std::result::Result<ObjectId, oid::Error> {
        ObjectId::with_string(&self.0)
    }
}

impl PartialEq<&str> for ID {
    fn eq(&self, other: &&str) -> bool {
        self.0.as_str() == *other
    }
}

/// Convert any type that implements Display to the ID type
pub trait ToGraphQLID {
    #[allow(missing_docs)]
    fn to_graphql_id(&self) -> ID;
}

impl<T> ToGraphQLID for T
where
    T: std::fmt::Display,
{
    fn to_graphql_id(&self) -> ID {
        ID(self.to_string())
    }
}

#[Scalar(internal)]
impl ScalarType for ID {
    fn type_name() -> &'static str {
        "ID"
    }

    fn parse(value: &Value) -> InputValueResult<Self> {
        match value {
            Value::Int(n) => Ok(ID(n.to_string())),
            Value::String(s) => Ok(ID(s.clone())),
            _ => Err(InputValueError::ExpectedType),
        }
    }

    fn is_valid(value: &Value) -> bool {
        match value {
            Value::Int(_) | Value::String(_) => true,
            _ => false,
        }
    }

    fn to_json(&self) -> Result<serde_json::Value> {
        Ok(self.0.clone().into())
    }
}