#![forbid(unsafe_code)]
#![warn(missing_docs)]
use std::{any::Any, fmt, marker::PhantomData, str::FromStr};
use async_graphql::{Error, InputValueError, InputValueResult, Scalar, ScalarType, Value};
pub use async_graphql_relay_derive::*;
use uuid::Uuid;
pub trait RelayNodeInterface
where
Self: Sized,
{
fn fetch_node(
ctx: RelayContext,
relay_id: String,
) -> impl std::future::Future<Output = Result<Self, Error>> + Send;
}
pub trait RelayNodeStruct {
const ID_SUFFIX: &'static str;
}
pub trait RelayNode: RelayNodeStruct {
type TNode: RelayNodeInterface;
fn get(
ctx: RelayContext,
id: RelayNodeID<Self>,
) -> impl std::future::Future<Output = Result<Option<Self::TNode>, Error>> + Send;
}
#[derive(Clone, PartialEq, Eq)]
pub struct RelayNodeID<T: RelayNode + ?Sized>(Uuid, PhantomData<T>);
impl<T: RelayNode> RelayNodeID<T> {
pub fn new(uuid: Uuid) -> Self {
RelayNodeID(uuid, PhantomData)
}
pub fn new_from_relay_id(relay_id: String) -> Result<Self, Error> {
if relay_id.len() < 32 {
return Err(Error::new("Invalid id provided to node query!"));
}
let (id, _) = relay_id.split_at(32);
let uuid = Uuid::parse_str(id)
.map_err(|_err| Error::new("Invalid id provided to node query!"))?;
Ok(RelayNodeID(uuid, PhantomData))
}
pub fn new_from_str(uuid: &str) -> Result<Self, uuid::Error> {
Ok(Self::new(Uuid::from_str(uuid)?))
}
pub fn to_uuid(&self) -> Uuid {
self.0
}
}
impl<T: RelayNode> From<&RelayNodeID<T>> for String {
fn from(id: &RelayNodeID<T>) -> Self {
format!("{}{}", id.0.simple(), T::ID_SUFFIX)
}
}
impl<T: RelayNode> std::fmt::Display for RelayNodeID<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", String::from(self))
}
}
impl<T: RelayNode> fmt::Debug for RelayNodeID<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("RelayNodeID").field(&self.0).finish()
}
}
#[Scalar]
impl<T: RelayNode + Send + Sync> ScalarType for RelayNodeID<T> {
fn parse(value: Value) -> InputValueResult<Self> {
match value {
Value::String(s) => Ok(RelayNodeID::<T>::new_from_str(&s)?),
_ => Err(InputValueError::expected_type(value)),
}
}
fn to_value(&self) -> Value {
Value::String(String::from(self))
}
}
pub struct RelayContext(Box<dyn Any + Sync + Send>);
impl RelayContext {
pub fn new<T: Any + Sync + Send>(data: T) -> Self {
Self(Box::new(data))
}
pub fn nil() -> Self {
let nil: Option<()> = None;
Self(Box::new(nil))
}
pub fn get<T: Any + Sync + Send>(&self) -> Option<&T> {
match self.0.downcast_ref::<T>() {
Some(v) => Some(v),
_ => None,
}
}
}
#[cfg(feature = "sea-orm")]
impl<T: RelayNode> From<RelayNodeID<T>> for sea_orm::Value {
fn from(source: RelayNodeID<T>) -> Self {
sea_orm::Value::Uuid(Some(Box::new(source.to_uuid())))
}
}
#[cfg(feature = "sea-orm")]
impl<T: RelayNode> sea_orm::TryGetable for RelayNodeID<T> {
fn try_get_by<I: sea_orm::ColIdx>(
res: &sea_orm::QueryResult,
index: I,
) -> Result<Self, sea_orm::TryGetError> {
let val: Uuid = res.try_get_by(index).map_err(sea_orm::TryGetError::DbErr)?;
Ok(RelayNodeID::<T>::new(val))
}
}
#[cfg(feature = "sea-orm")]
impl<T: RelayNode> sea_orm::sea_query::Nullable for RelayNodeID<T> {
fn null() -> sea_orm::Value {
sea_orm::Value::Uuid(None)
}
}
#[cfg(feature = "sea-orm")]
impl<T: RelayNode> sea_orm::sea_query::ValueType for RelayNodeID<T> {
fn try_from(v: sea_orm::Value) -> Result<Self, sea_orm::sea_query::ValueTypeErr> {
match v {
sea_orm::Value::Uuid(Some(x)) => Ok(RelayNodeID::<T>::new(*x)),
_ => Err(sea_orm::sea_query::ValueTypeErr),
}
}
fn type_name() -> String {
stringify!(Uuid).to_owned()
}
fn column_type() -> sea_orm::sea_query::ColumnType {
sea_orm::sea_query::ColumnType::Uuid
}
fn array_type() -> sea_orm::sea_query::ArrayType {
sea_orm::sea_query::ArrayType::Uuid
}
}
#[cfg(feature = "sea-orm")]
impl<T: RelayNode> sea_orm::TryFromU64 for RelayNodeID<T> {
fn try_from_u64(_: u64) -> Result<Self, sea_orm::DbErr> {
Err(sea_orm::DbErr::Exec(sea_orm::error::RuntimeErr::Internal(
format!(
"{} cannot be converted from u64",
std::any::type_name::<T>()
),
)))
}
}
#[cfg(feature = "serde")]
impl<T: RelayNode> serde::Serialize for RelayNodeID<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&self.to_string())
}
}