The scalar name and description attributes are placed on the process macro attributes. #97
This commit is contained in:
parent
8fb5c6c9cd
commit
89bfaac0eb
|
@ -848,22 +848,53 @@ impl DataSource {
|
|||
|
||||
pub struct Scalar {
|
||||
pub internal: bool,
|
||||
pub name: Option<String>,
|
||||
pub desc: Option<String>,
|
||||
}
|
||||
|
||||
impl Scalar {
|
||||
pub fn parse(args: AttributeArgs) -> Result<Self> {
|
||||
let mut internal = false;
|
||||
let mut name = None;
|
||||
let mut desc = None;
|
||||
|
||||
for arg in args {
|
||||
match arg {
|
||||
NestedMeta::Meta(Meta::Path(p)) if p.is_ident("internal") => {
|
||||
internal = true;
|
||||
NestedMeta::Meta(Meta::Path(p)) => {
|
||||
if p.is_ident("internal") {
|
||||
internal = true;
|
||||
}
|
||||
}
|
||||
NestedMeta::Meta(Meta::NameValue(nv)) => {
|
||||
if nv.path.is_ident("name") {
|
||||
if let syn::Lit::Str(lit) = nv.lit {
|
||||
name = Some(lit.value());
|
||||
} else {
|
||||
return Err(Error::new_spanned(
|
||||
&nv.lit,
|
||||
"Attribute 'name' should be a string.",
|
||||
));
|
||||
}
|
||||
} else if nv.path.is_ident("desc") {
|
||||
if let syn::Lit::Str(lit) = nv.lit {
|
||||
desc = Some(lit.value());
|
||||
} else {
|
||||
return Err(Error::new_spanned(
|
||||
&nv.lit,
|
||||
"Attribute 'desc' should be a string.",
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self { internal })
|
||||
Ok(Self {
|
||||
internal,
|
||||
name,
|
||||
desc,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ mod input_object;
|
|||
mod interface;
|
||||
mod object;
|
||||
mod output_type;
|
||||
mod scalar;
|
||||
mod simple_object;
|
||||
mod subscription;
|
||||
mod union;
|
||||
|
@ -141,46 +142,9 @@ pub fn Scalar(args: TokenStream, input: TokenStream) -> TokenStream {
|
|||
Ok(scalar_args) => scalar_args,
|
||||
Err(err) => return err.to_compile_error().into(),
|
||||
};
|
||||
let input2: proc_macro2::TokenStream = input.clone().into();
|
||||
let item_impl = parse_macro_input!(input as ItemImpl);
|
||||
let self_ty = &item_impl.self_ty;
|
||||
let generic = &item_impl.generics;
|
||||
let where_clause = &item_impl.generics.where_clause;
|
||||
let crate_name = get_crate_name(scalar_args.internal);
|
||||
let expanded = quote! {
|
||||
#input2
|
||||
|
||||
impl #generic #crate_name::Type for #self_ty #where_clause {
|
||||
fn type_name() -> std::borrow::Cow<'static, str> {
|
||||
std::borrow::Cow::Borrowed(<#self_ty as #crate_name::ScalarType>::type_name())
|
||||
}
|
||||
|
||||
fn create_type_info(registry: &mut #crate_name::registry::Registry) -> String {
|
||||
registry.create_type::<#self_ty, _>(|_| #crate_name::registry::MetaType::Scalar {
|
||||
name: <#self_ty as #crate_name::ScalarType>::type_name().to_string(),
|
||||
description: <#self_ty>::description(),
|
||||
is_valid: |value| <#self_ty as #crate_name::ScalarType>::is_valid(value),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl #generic #crate_name::InputValueType for #self_ty #where_clause {
|
||||
fn parse(value: #crate_name::Value) -> #crate_name::InputValueResult<Self> {
|
||||
<#self_ty as #crate_name::ScalarType>::parse(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::ptr_arg)]
|
||||
#[#crate_name::async_trait::async_trait]
|
||||
impl #generic #crate_name::OutputValueType for #self_ty #where_clause {
|
||||
async fn resolve(
|
||||
&self,
|
||||
_: &#crate_name::ContextSelectionSet<'_>,
|
||||
_pos: #crate_name::Pos,
|
||||
) -> #crate_name::Result<#crate_name::serde_json::Value> {
|
||||
self.to_json()
|
||||
}
|
||||
}
|
||||
};
|
||||
expanded.into()
|
||||
let mut item_impl = parse_macro_input!(input as ItemImpl);
|
||||
match scalar::generate(&scalar_args, &mut item_impl) {
|
||||
Ok(expanded) => expanded,
|
||||
Err(err) => err.to_compile_error().into(),
|
||||
}
|
||||
}
|
||||
|
|
68
async-graphql-derive/src/scalar.rs
Normal file
68
async-graphql-derive/src/scalar.rs
Normal file
|
@ -0,0 +1,68 @@
|
|||
use crate::args;
|
||||
use crate::utils::{check_reserved_name, get_crate_name, get_rustdoc};
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{Error, ItemImpl, Result, Type};
|
||||
|
||||
pub fn generate(scalar_args: &args::Scalar, item_impl: &mut ItemImpl) -> Result<TokenStream> {
|
||||
let self_name = match item_impl.self_ty.as_ref() {
|
||||
Type::Path(path) => path
|
||||
.path
|
||||
.segments
|
||||
.last()
|
||||
.map(|s| s.ident.to_string())
|
||||
.unwrap(),
|
||||
_ => return Err(Error::new_spanned(&item_impl.self_ty, "Invalid type")),
|
||||
};
|
||||
let gql_typename = scalar_args
|
||||
.name
|
||||
.clone()
|
||||
.unwrap_or_else(|| self_name.clone());
|
||||
check_reserved_name(&gql_typename, scalar_args.internal)?;
|
||||
let desc = scalar_args
|
||||
.desc
|
||||
.clone()
|
||||
.or_else(|| get_rustdoc(&item_impl.attrs).ok().flatten())
|
||||
.map(|s| quote! { Some(#s) })
|
||||
.unwrap_or_else(|| quote! {None});
|
||||
let self_ty = &item_impl.self_ty;
|
||||
let generic = &item_impl.generics;
|
||||
let where_clause = &item_impl.generics.where_clause;
|
||||
let crate_name = get_crate_name(scalar_args.internal);
|
||||
let expanded = quote! {
|
||||
#item_impl
|
||||
|
||||
impl #generic #crate_name::Type for #self_ty #where_clause {
|
||||
fn type_name() -> std::borrow::Cow<'static, str> {
|
||||
std::borrow::Cow::Borrowed(#gql_typename)
|
||||
}
|
||||
|
||||
fn create_type_info(registry: &mut #crate_name::registry::Registry) -> String {
|
||||
registry.create_type::<#self_ty, _>(|_| #crate_name::registry::MetaType::Scalar {
|
||||
name: #gql_typename.to_string(),
|
||||
description: #desc,
|
||||
is_valid: |value| <#self_ty as #crate_name::ScalarType>::is_valid(value),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl #generic #crate_name::InputValueType for #self_ty #where_clause {
|
||||
fn parse(value: #crate_name::Value) -> #crate_name::InputValueResult<Self> {
|
||||
<#self_ty as #crate_name::ScalarType>::parse(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::ptr_arg)]
|
||||
#[#crate_name::async_trait::async_trait]
|
||||
impl #generic #crate_name::OutputValueType for #self_ty #where_clause {
|
||||
async fn resolve(
|
||||
&self,
|
||||
_: &#crate_name::ContextSelectionSet<'_>,
|
||||
_pos: #crate_name::Pos,
|
||||
) -> #crate_name::Result<#crate_name::serde_json::Value> {
|
||||
self.to_json()
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(expanded.into())
|
||||
}
|
|
@ -14,11 +14,6 @@ struct StringNumber(i64);
|
|||
|
||||
#[Scalar]
|
||||
impl ScalarType for StringNumber {
|
||||
fn type_name() -> &'static str {
|
||||
// Name of type
|
||||
"StringNumber"
|
||||
}
|
||||
|
||||
fn parse(value: Value) -> InputValueResult<Self> {
|
||||
if let Value::String(value) = value {
|
||||
// Parse the integer value
|
||||
|
|
|
@ -14,11 +14,6 @@ struct StringNumber(i64);
|
|||
|
||||
#[Scalar]
|
||||
impl ScalarType for StringNumber {
|
||||
fn type_name() -> &'static str {
|
||||
// 类型名
|
||||
"StringNumber"
|
||||
}
|
||||
|
||||
fn parse(value: Value) -> InputValueResult<Self> {
|
||||
if let Value::String(value) = value {
|
||||
// 解析整数
|
||||
|
|
12
src/base.rs
12
src/base.rs
|
@ -124,10 +124,6 @@ pub trait InputObjectType: InputValueType {}
|
|||
///
|
||||
/// #[Scalar]
|
||||
/// impl ScalarType for MyInt {
|
||||
/// fn type_name() -> &'static str {
|
||||
/// "MyInt"
|
||||
/// }
|
||||
///
|
||||
/// fn parse(value: Value) -> InputValueResult<Self> {
|
||||
/// if let Value::Int(n) = value {
|
||||
/// Ok(MyInt(n as i32))
|
||||
|
@ -142,14 +138,6 @@ pub trait InputObjectType: InputValueType {}
|
|||
/// }
|
||||
/// ```
|
||||
pub trait ScalarType: Sized + Send {
|
||||
/// The type name of a scalar.
|
||||
fn type_name() -> &'static str;
|
||||
|
||||
/// The description of a scalar.
|
||||
fn description() -> Option<&'static str> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Parse a scalar value, return `Some(Self)` if successful, otherwise return `None`.
|
||||
fn parse(value: Value) -> InputValueResult<Self>;
|
||||
|
||||
|
|
|
@ -656,4 +656,12 @@ pub use async_graphql_derive::Subscription;
|
|||
pub use async_graphql_derive::DataSource;
|
||||
|
||||
/// Define a Scalar
|
||||
///
|
||||
/// # Macro parameters
|
||||
///
|
||||
/// | Attribute | description | Type | Optional |
|
||||
/// |-------------|---------------------------|----------|----------|
|
||||
/// | name | Scalar name | string | Y |
|
||||
/// | desc | Scalar description | string | Y |
|
||||
///
|
||||
pub use async_graphql_derive::Scalar;
|
||||
|
|
|
@ -8,16 +8,9 @@ use serde::de::DeserializeOwned;
|
|||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub struct Any(pub Value);
|
||||
|
||||
#[Scalar(internal)]
|
||||
/// The `_Any` scalar is used to pass representations of entities from external services into the root `_entities` field for execution.
|
||||
#[Scalar(internal, name = "_Any")]
|
||||
impl ScalarType for Any {
|
||||
fn type_name() -> &'static str {
|
||||
"_Any"
|
||||
}
|
||||
|
||||
fn description() -> Option<&'static str> {
|
||||
Some("The `_Any` scalar is used to pass representations of entities from external services into the root `_entities` field for execution.")
|
||||
}
|
||||
|
||||
fn parse(value: Value) -> InputValueResult<Self> {
|
||||
Ok(Self(value))
|
||||
}
|
||||
|
|
|
@ -1,16 +1,9 @@
|
|||
use crate::{InputValueError, InputValueResult, Result, ScalarType, Value};
|
||||
use async_graphql_derive::Scalar;
|
||||
|
||||
#[Scalar(internal)]
|
||||
/// The `Boolean` scalar type represents `true` or `false`.
|
||||
#[Scalar(internal, name = "Boolean")]
|
||||
impl ScalarType for bool {
|
||||
fn type_name() -> &'static str {
|
||||
"Boolean"
|
||||
}
|
||||
|
||||
fn description() -> Option<&'static str> {
|
||||
Some("The `Boolean` scalar type represents `true` or `false`.")
|
||||
}
|
||||
|
||||
fn parse(value: Value) -> InputValueResult<Self> {
|
||||
match value {
|
||||
Value::Boolean(n) => Ok(n),
|
||||
|
|
|
@ -5,10 +5,6 @@ use chrono::{DateTime, Utc};
|
|||
|
||||
#[Scalar(internal)]
|
||||
impl ScalarType for ObjectId {
|
||||
fn type_name() -> &'static str {
|
||||
"ObjectId"
|
||||
}
|
||||
|
||||
fn parse(value: Value) -> InputValueResult<Self> {
|
||||
match value {
|
||||
Value::String(s) => Ok(ObjectId::with_string(&s)?),
|
||||
|
@ -21,12 +17,8 @@ impl ScalarType for ObjectId {
|
|||
}
|
||||
}
|
||||
|
||||
#[Scalar(internal)]
|
||||
#[Scalar(internal, name = "DateTime")]
|
||||
impl ScalarType for UtcDateTime {
|
||||
fn type_name() -> &'static str {
|
||||
"DateTime"
|
||||
}
|
||||
|
||||
fn parse(value: Value) -> InputValueResult<Self> {
|
||||
DateTime::<Utc>::parse(value).map(UtcDateTime::from)
|
||||
}
|
||||
|
|
|
@ -3,12 +3,8 @@ use async_graphql_derive::Scalar;
|
|||
use chrono_tz::Tz;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[Scalar(internal)]
|
||||
#[Scalar(internal, name = "TimeZone")]
|
||||
impl ScalarType for Tz {
|
||||
fn type_name() -> &'static str {
|
||||
"TimeZone"
|
||||
}
|
||||
|
||||
fn parse(value: Value) -> InputValueResult<Self> {
|
||||
match value {
|
||||
Value::String(s) => Ok(Tz::from_str(&s)?),
|
||||
|
|
|
@ -5,12 +5,8 @@ use chrono::{DateTime, TimeZone, Utc};
|
|||
/// Implement the DateTime<Utc> scalar
|
||||
///
|
||||
/// The input/output is a string in RFC3339 format.
|
||||
#[Scalar(internal)]
|
||||
#[Scalar(internal, name = "DateTime")]
|
||||
impl ScalarType for DateTime<Utc> {
|
||||
fn type_name() -> &'static str {
|
||||
"DateTime"
|
||||
}
|
||||
|
||||
fn parse(value: Value) -> InputValueResult<Self> {
|
||||
match value {
|
||||
Value::String(s) => Ok(Utc.datetime_from_str(&s, "%+")?),
|
||||
|
|
|
@ -4,16 +4,9 @@ use async_graphql_derive::Scalar;
|
|||
macro_rules! impl_float_scalars {
|
||||
($($ty:ty),*) => {
|
||||
$(
|
||||
#[Scalar(internal)]
|
||||
/// The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](https://en.wikipedia.org/wiki/IEEE_floating_point).
|
||||
#[Scalar(internal, name = "Float")]
|
||||
impl ScalarType for $ty {
|
||||
fn type_name() -> &'static str {
|
||||
"Float"
|
||||
}
|
||||
|
||||
fn description() -> Option<&'static str> {
|
||||
Some("The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](https://en.wikipedia.org/wiki/IEEE_floating_point).")
|
||||
}
|
||||
|
||||
fn parse(value: Value) -> InputValueResult<Self> {
|
||||
match value {
|
||||
Value::Int(n) => Ok(n as Self),
|
||||
|
|
|
@ -75,10 +75,6 @@ impl PartialEq<&str> for ID {
|
|||
|
||||
#[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())),
|
||||
|
|
|
@ -4,16 +4,9 @@ use async_graphql_derive::Scalar;
|
|||
macro_rules! impl_integer_scalars {
|
||||
($($ty:ty),*) => {
|
||||
$(
|
||||
#[Scalar(internal)]
|
||||
/// The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.
|
||||
#[Scalar(internal, name = "Int")]
|
||||
impl ScalarType for $ty {
|
||||
fn type_name() -> &'static str {
|
||||
"Int"
|
||||
}
|
||||
|
||||
fn description() -> Option<&'static str> {
|
||||
Some("The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.")
|
||||
}
|
||||
|
||||
fn parse(value: Value) -> InputValueResult<Self> {
|
||||
match value {
|
||||
Value::Int(n) => Ok(n as Self),
|
||||
|
@ -41,16 +34,9 @@ impl_integer_scalars!(i8, i16, i32, u8, u16, u32);
|
|||
macro_rules! impl_int64_scalars {
|
||||
($($ty:ty),*) => {
|
||||
$(
|
||||
#[Scalar(internal)]
|
||||
/// The `Int64` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^64) and 2^64 - 1.
|
||||
#[Scalar(internal, name = "Int64")]
|
||||
impl ScalarType for $ty {
|
||||
fn type_name() -> &'static str {
|
||||
"Int64"
|
||||
}
|
||||
|
||||
fn description() -> Option<&'static str> {
|
||||
Some("The `Int64` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^64) and 2^64 - 1.")
|
||||
}
|
||||
|
||||
fn parse(value: Value) -> InputValueResult<Self> {
|
||||
match value {
|
||||
Value::Int(n) => Ok(n as Self),
|
||||
|
|
|
@ -22,12 +22,9 @@ impl<T> DerefMut for Json<T> {
|
|||
}
|
||||
}
|
||||
|
||||
#[Scalar(internal)]
|
||||
/// A scalar that can represent any JSON value.
|
||||
#[Scalar(internal, name = "JSON")]
|
||||
impl<T: DeserializeOwned + Serialize + Send + Sync> ScalarType for Json<T> {
|
||||
fn type_name() -> &'static str {
|
||||
"JSON"
|
||||
}
|
||||
|
||||
fn parse(value: Value) -> InputValueResult<Self> {
|
||||
Ok(serde_json::from_value(value.into()).map(Json)?)
|
||||
}
|
||||
|
|
|
@ -2,13 +2,8 @@ use crate::{InputValueError, InputValueResult, Result, ScalarType, Value};
|
|||
use async_graphql_derive::Scalar;
|
||||
use chrono::NaiveDate;
|
||||
|
||||
/// Implement the NaiveDate scalar
|
||||
#[Scalar(internal)]
|
||||
impl ScalarType for NaiveDate {
|
||||
fn type_name() -> &'static str {
|
||||
"NaiveDate"
|
||||
}
|
||||
|
||||
fn parse(value: Value) -> InputValueResult<Self> {
|
||||
match value {
|
||||
Value::String(s) => Ok(NaiveDate::parse_from_str(&s, "%Y-%m-%d")?),
|
||||
|
|
|
@ -2,13 +2,8 @@ use crate::{InputValueError, InputValueResult, Result, ScalarType, Value};
|
|||
use async_graphql_derive::Scalar;
|
||||
use chrono::NaiveTime;
|
||||
|
||||
/// Implement the NaiveTime scalar
|
||||
#[Scalar(internal)]
|
||||
impl ScalarType for NaiveTime {
|
||||
fn type_name() -> &'static str {
|
||||
"NaiveTime"
|
||||
}
|
||||
|
||||
fn parse(value: Value) -> InputValueResult<Self> {
|
||||
match value {
|
||||
Value::String(s) => Ok(NaiveTime::parse_from_str(&s, "%H:%M:%S")?),
|
||||
|
|
|
@ -6,18 +6,9 @@ use crate::{
|
|||
use async_graphql_derive::Scalar;
|
||||
use std::borrow::Cow;
|
||||
|
||||
const STRING_DESC: &str = "The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.";
|
||||
|
||||
/// The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.
|
||||
#[Scalar(internal)]
|
||||
impl ScalarType for String {
|
||||
fn type_name() -> &'static str {
|
||||
"String"
|
||||
}
|
||||
|
||||
fn description() -> Option<&'static str> {
|
||||
Some(STRING_DESC)
|
||||
}
|
||||
|
||||
fn parse(value: Value) -> InputValueResult<Self> {
|
||||
match value {
|
||||
Value::String(s) => Ok(s),
|
||||
|
@ -43,14 +34,7 @@ impl<'a> Type for &'a str {
|
|||
}
|
||||
|
||||
fn create_type_info(registry: &mut registry::Registry) -> String {
|
||||
registry.create_type::<Self, _>(|_| registry::MetaType::Scalar {
|
||||
name: Self::type_name().to_string(),
|
||||
description: Some(STRING_DESC),
|
||||
is_valid: |value| match value {
|
||||
Value::String(_) => true,
|
||||
_ => false,
|
||||
},
|
||||
})
|
||||
<String as Type>::create_type_info(registry)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,10 +4,6 @@ use url::Url;
|
|||
|
||||
#[Scalar(internal)]
|
||||
impl ScalarType for Url {
|
||||
fn type_name() -> &'static str {
|
||||
"Url"
|
||||
}
|
||||
|
||||
fn parse(value: Value) -> InputValueResult<Self> {
|
||||
match value {
|
||||
Value::String(s) => Ok(Url::parse(&s)?),
|
||||
|
|
|
@ -2,12 +2,8 @@ use crate::{InputValueError, InputValueResult, Result, ScalarType, Value};
|
|||
use async_graphql_derive::Scalar;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[Scalar(internal)]
|
||||
#[Scalar(internal, name = "UUID")]
|
||||
impl ScalarType for Uuid {
|
||||
fn type_name() -> &'static str {
|
||||
"UUID"
|
||||
}
|
||||
|
||||
fn parse(value: Value) -> InputValueResult<Self> {
|
||||
match value {
|
||||
Value::String(s) => Ok(Uuid::parse_str(&s)?),
|
||||
|
|
|
@ -40,10 +40,6 @@ impl From<ID> for Cursor {
|
|||
|
||||
#[Scalar(internal)]
|
||||
impl ScalarType for Cursor {
|
||||
fn type_name() -> &'static str {
|
||||
"Cursor"
|
||||
}
|
||||
|
||||
fn parse(value: Value) -> InputValueResult<Self> {
|
||||
match value {
|
||||
Value::String(s) => Ok(Cursor(s)),
|
||||
|
|
|
@ -75,10 +75,6 @@ struct TestScalar(i32);
|
|||
|
||||
#[Scalar(desc = "Test scalar")]
|
||||
impl ScalarType for TestScalar {
|
||||
fn type_name() -> &'static str {
|
||||
"TestScalar"
|
||||
}
|
||||
|
||||
fn parse(_value: Value) -> InputValueResult<Self> {
|
||||
Ok(TestScalar(42))
|
||||
}
|
||||
|
@ -689,7 +685,7 @@ pub async fn test_introspection_scalar() {
|
|||
"__type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "TestScalar",
|
||||
"description": null,
|
||||
"description": "Test scalar",
|
||||
}
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user