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 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
//! This module contains traits that abstract over GraphQL implementations,
//! allowing this library to be used with different GraphQL client libraries.
//!
//! Support is provided for [`cynic`][cynic] & [`graphql_client`][graphql-client],
//! but other client libraries can be added by implementing these traits for
//! those libraries.
//!
//! [cynic]: https://cynic-rs.dev
//! [graphql-client]: https://github.com/graphql-rust/graphql-client
/// A trait for GraphQL clients.
pub trait GraphqlClient {
/// The generic response type for this GraphqlClient implementation
///
/// Our client will decode this, then pass it to a `GraphqlOperation` for decoding
/// to the specific response type of the GraphqlOperation.
type Response: serde::de::DeserializeOwned + Send;
/// The error that will be returned from failed attempts to decode a `Response`.
type DecodeError: std::error::Error + Send + 'static;
/// Decodes some error JSON into a `Response`
fn error_response(errors: Vec<serde_json::Value>) -> Result<Self::Response, Self::DecodeError>;
}
/// An abstraction over GraphQL operations.
pub trait GraphqlOperation: serde::Serialize {
/// The "generic" response type. A graphql-ws-client supports running multiple
/// operations at a time. This GenericResponse is what will intially be decoded -
/// with the `decode` function converting this into the actual operation response.
///
/// This type needs to match up with [GraphqlClient::Response].
type GenericResponse;
/// The actual response & error type of this operation.
type Response;
/// The error that will be returned from failed attempts to decode a `Response`.
type Error: std::error::Error;
/// Decodes a `GenericResponse` into the actual response that will be returned
/// to users for this operation.
fn decode(&self, data: Self::GenericResponse) -> Result<Self::Response, Self::Error>;
}
#[cfg(feature = "cynic")]
pub use self::cynic::Cynic;
#[cfg(feature = "cynic")]
mod cynic {
use super::*;
/// Provides an implementation of [GraphqlClient] for the cynic GraphQL crate
pub struct Cynic {}
impl GraphqlClient for Cynic {
type Response = ::cynic::GraphQlResponse<serde_json::Value>;
type DecodeError = serde_json::Error;
fn error_response(
errors: Vec<serde_json::Value>,
) -> Result<Self::Response, Self::DecodeError> {
Ok(::cynic::GraphQlResponse {
data: None,
errors: Some(
errors
.into_iter()
.map(serde_json::from_value)
.collect::<Result<Vec<_>, _>>()?,
),
})
}
}
impl<ResponseData, Variables> GraphqlOperation
for ::cynic::StreamingOperation<ResponseData, Variables>
where
ResponseData: serde::de::DeserializeOwned,
Variables: serde::Serialize,
{
type GenericResponse = ::cynic::GraphQlResponse<serde_json::Value>;
type Response = ::cynic::GraphQlResponse<ResponseData>;
type Error = serde_json::Error;
fn decode(&self, response: Self::GenericResponse) -> Result<Self::Response, Self::Error> {
Ok(::cynic::GraphQlResponse {
data: response
.data
.map(|data| serde_json::from_value(data))
.transpose()?,
errors: response.errors,
})
}
}
}
#[cfg(feature = "graphql_client")]
pub use self::graphql_client::GraphQLClient;
#[cfg(feature = "graphql_client")]
pub use self::graphql_client::StreamingOperation;
#[cfg(feature = "graphql_client")]
mod graphql_client {
use super::*;
use ::graphql_client::{GraphQLQuery, QueryBody, Response};
use std::marker::PhantomData;
/// Provides an implementation of [GraphqlClient] for the graphql_client GraphQL crate
pub struct GraphQLClient {}
impl GraphqlClient for GraphQLClient {
type Response = Response<serde_json::Value>;
type DecodeError = serde_json::Error;
fn error_response(
errors: Vec<serde_json::Value>,
) -> Result<Self::Response, Self::DecodeError> {
Ok(Response {
data: None,
errors: Some(
errors
.into_iter()
.map(serde_json::from_value)
.collect::<Result<Vec<_>, _>>()?,
),
extensions: None,
})
}
}
/// A streaming operation for a GraphQLQuery
pub struct StreamingOperation<Q: GraphQLQuery> {
inner: QueryBody<Q::Variables>,
phantom: PhantomData<Q>,
}
impl<Q: GraphQLQuery> StreamingOperation<Q> {
/// Constructs a StreamingOperation
pub fn new(variables: Q::Variables) -> Self {
Self {
inner: Q::build_query(variables),
phantom: PhantomData::default(),
}
}
fn decode_response(
&self,
response: Response<serde_json::Value>,
) -> Result<Response<Q::ResponseData>, serde_json::Error> {
if let Some(data) = response.data {
Ok(::graphql_client::Response {
data: Some(serde_json::from_value(data)?),
errors: response.errors,
extensions: response.extensions,
})
} else {
Ok(::graphql_client::Response {
data: None,
errors: response.errors,
extensions: response.extensions,
})
}
}
}
impl<Q: GraphQLQuery> serde::Serialize for StreamingOperation<Q> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.inner.serialize(serializer)
}
}
impl<Q: GraphQLQuery> GraphqlOperation for StreamingOperation<Q> {
type GenericResponse = Response<serde_json::Value>;
type Response = Response<Q::ResponseData>;
type Error = serde_json::Error;
fn decode(&self, response: Self::GenericResponse) -> Result<Self::Response, Self::Error> {
self.decode_response(response)
}
}
}