The scalar name and description attributes are placed on the process macro attributes. #97

This commit is contained in:
Sunli 2020-05-19 13:27:01 +08:00
parent 8fb5c6c9cd
commit 89bfaac0eb
23 changed files with 135 additions and 186 deletions

View File

@ -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,
})
}
}

View File

@ -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(),
}
}

View 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())
}

View File

@ -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

View File

@ -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 {
// 解析整数

View File

@ -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>;

View File

@ -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;

View File

@ -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))
}

View File

@ -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),

View File

@ -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)
}

View File

@ -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)?),

View File

@ -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, "%+")?),

View File

@ -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),

View File

@ -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())),

View File

@ -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),

View File

@ -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)?)
}

View File

@ -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")?),

View File

@ -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")?),

View File

@ -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)
}
}

View File

@ -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)?),

View File

@ -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)?),

View File

@ -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)),

View File

@ -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",
}
});