use std::fmt::{self, Display, Formatter}; use darling::ast::{Data, Fields}; use darling::util::{Ignored, SpannedValue}; use darling::{FromDeriveInput, FromField, FromMeta, FromVariant}; use inflector::Inflector; use syn::{ Attribute, Generics, Ident, Lit, LitBool, LitStr, Meta, NestedMeta, Path, Type, Visibility, }; use crate::validators::Validators; #[derive(FromMeta, Clone)] #[darling(default)] pub struct CacheControl { public: bool, private: bool, pub max_age: usize, } impl Default for CacheControl { fn default() -> Self { Self { public: true, private: false, max_age: 0, } } } impl CacheControl { pub fn is_public(&self) -> bool { !self.private && self.public } } #[derive(Debug)] pub enum DefaultValue { Default, Value(Lit), } impl FromMeta for DefaultValue { fn from_word() -> darling::Result { Ok(DefaultValue::Default) } fn from_value(value: &Lit) -> darling::Result { Ok(DefaultValue::Value(value.clone())) } } #[derive(Debug, Clone)] pub enum Visible { None, HiddenAlways, FnName(Path), } impl FromMeta for Visible { fn from_value(value: &Lit) -> darling::Result { match value { Lit::Bool(LitBool { value: true, .. }) => Ok(Visible::None), Lit::Bool(LitBool { value: false, .. }) => Ok(Visible::HiddenAlways), Lit::Str(str) => Ok(Visible::FnName(syn::parse_str::(&str.value())?)), _ => Err(darling::Error::unexpected_lit_type(value)), } } } pub struct PathList(pub Vec); impl FromMeta for PathList { fn from_list(items: &[NestedMeta]) -> darling::Result { let mut res = Vec::new(); for item in items { if let NestedMeta::Meta(Meta::Path(p)) = item { res.push(p.clone()); } else { return Err(darling::Error::custom("Invalid path list")); } } Ok(PathList(res)) } } #[derive(FromMeta)] pub struct ConcreteType { pub name: String, pub params: PathList, } #[derive(Debug, Clone)] pub enum Deprecation { NoDeprecated, Deprecated { reason: Option }, } impl Default for Deprecation { fn default() -> Self { Deprecation::NoDeprecated } } impl FromMeta for Deprecation { fn from_word() -> darling::Result { Ok(Deprecation::Deprecated { reason: None }) } fn from_value(value: &Lit) -> darling::Result { match value { Lit::Bool(LitBool { value: true, .. }) => Ok(Deprecation::Deprecated { reason: None }), Lit::Bool(LitBool { value: false, .. }) => Ok(Deprecation::NoDeprecated), Lit::Str(str) => Ok(Deprecation::Deprecated { reason: Some(str.value()), }), _ => Err(darling::Error::unexpected_lit_type(value)), } } } #[derive(FromField)] #[darling(attributes(graphql), forward_attrs(doc))] pub struct SimpleObjectField { pub ident: Option, pub ty: Type, pub vis: Visibility, pub attrs: Vec, #[darling(default)] pub skip: bool, #[darling(default)] pub skip_output: bool, // for InputObject #[darling(default)] pub skip_input: bool, #[darling(default)] pub name: Option, #[darling(default)] pub deprecation: Deprecation, #[darling(default)] pub owned: bool, #[darling(default)] pub cache_control: CacheControl, #[darling(default)] pub external: bool, #[darling(default)] pub provides: Option, #[darling(default)] pub requires: Option, #[darling(default)] pub guard: Option>, #[darling(default)] pub visible: Option, #[darling(default, multiple)] pub derived: Vec, // for InputObject #[darling(default)] pub default: Option, #[darling(default)] pub default_with: Option, #[darling(default)] pub validator: Option, #[darling(default)] pub flatten: bool, #[darling(default)] pub secret: bool, } #[derive(FromDeriveInput)] #[darling(attributes(graphql), forward_attrs(doc))] pub struct SimpleObject { pub ident: Ident, pub generics: Generics, pub attrs: Vec, pub data: Data, #[darling(default)] pub internal: bool, #[darling(default)] pub fake: bool, #[darling(default)] pub complex: bool, #[darling(default)] pub name: Option, #[darling(default)] pub rename_fields: Option, #[darling(default)] pub rename_args: Option, #[darling(default)] pub cache_control: CacheControl, #[darling(default)] pub extends: bool, #[darling(default)] pub visible: Option, #[darling(default, multiple, rename = "concrete")] pub concretes: Vec, #[darling(default)] pub serial: bool, // for InputObject #[darling(default)] pub input_name: Option, } #[derive(FromMeta, Default)] #[darling(default)] pub struct Argument { pub name: Option, pub desc: Option, pub default: Option, pub default_with: Option, pub validator: Option, pub key: bool, // for entity pub visible: Option, pub secret: bool, } #[derive(FromMeta, Default)] #[darling(default)] pub struct Object { pub internal: bool, pub name: Option, pub rename_fields: Option, pub rename_args: Option, pub cache_control: CacheControl, pub extends: bool, pub use_type_description: bool, pub visible: Option, pub serial: bool, #[darling(multiple, rename = "concrete")] pub concretes: Vec, } pub enum ComplexityType { Const(usize), Fn(String), } impl FromMeta for ComplexityType { fn from_value(value: &Lit) -> darling::Result { match value { Lit::Int(n) => { let n = n.base10_parse::().unwrap(); if n < 0 { return Err(darling::Error::custom( "The complexity must be greater than or equal to 0.", )); } Ok(ComplexityType::Const(n as usize)) } Lit::Str(s) => Ok(ComplexityType::Fn(s.value())), _ => Err(darling::Error::unexpected_lit_type(value)), } } } #[derive(FromMeta, Default)] #[darling(default)] pub struct ObjectField { pub skip: bool, pub entity: bool, pub name: Option, pub deprecation: Deprecation, pub cache_control: CacheControl, pub external: bool, pub provides: Option, pub requires: Option, pub guard: Option>, pub visible: Option, pub complexity: Option, #[darling(default, multiple)] pub derived: Vec, pub flatten: bool, pub oneof: bool, } #[derive(FromMeta, Default, Clone)] #[darling(default)] /// Derivied fields arguments: are used to generate derivied fields. pub struct DerivedField { pub name: Option, pub into: Option, pub with: Option, #[darling(default)] pub owned: Option, } #[derive(FromDeriveInput)] #[darling(attributes(graphql), forward_attrs(doc))] pub struct Enum { pub ident: Ident, pub generics: Generics, pub attrs: Vec, pub data: Data, #[darling(default)] pub internal: bool, #[darling(default)] pub name: Option, #[darling(default)] pub rename_items: Option, #[darling(default)] pub remote: Option, #[darling(default)] pub visible: Option, } #[derive(FromVariant)] #[darling(attributes(graphql), forward_attrs(doc))] pub struct EnumItem { pub ident: Ident, pub attrs: Vec, pub fields: Fields, #[darling(default)] pub name: Option, #[darling(default)] pub deprecation: Deprecation, #[darling(default)] pub visible: Option, } #[derive(FromDeriveInput)] #[darling(attributes(graphql), forward_attrs(doc))] pub struct Union { pub ident: Ident, pub generics: Generics, pub attrs: Vec, pub data: Data, #[darling(default)] pub internal: bool, #[darling(default)] pub name: Option, #[darling(default)] pub visible: Option, } #[derive(FromVariant)] #[darling(attributes(graphql))] pub struct UnionItem { pub ident: Ident, pub fields: Fields, #[darling(default)] pub flatten: bool, } #[derive(FromField)] #[darling(attributes(graphql), forward_attrs(doc))] pub struct InputObjectField { pub ident: Option, pub ty: Type, pub vis: Visibility, pub attrs: Vec, #[darling(default)] pub name: Option, #[darling(default)] pub default: Option, #[darling(default)] pub default_with: Option, #[darling(default)] pub validator: Option, #[darling(default)] pub flatten: bool, #[darling(default)] pub skip: bool, #[darling(default)] pub skip_input: bool, // for SimpleObject #[darling(default)] pub skip_output: bool, #[darling(default)] pub visible: Option, #[darling(default)] pub secret: bool, } #[derive(FromDeriveInput)] #[darling(attributes(graphql), forward_attrs(doc))] pub struct InputObject { pub ident: Ident, pub generics: Generics, pub attrs: Vec, pub data: Data, #[darling(default)] pub internal: bool, #[darling(default)] pub name: Option, #[darling(default)] pub input_name: Option, #[darling(default)] pub rename_fields: Option, #[darling(default)] pub visible: Option, #[darling(default, multiple, rename = "concrete")] pub concretes: Vec, // for SimpleObject #[darling(default)] pub complex: bool, } #[derive(FromVariant)] #[darling(attributes(graphql), forward_attrs(doc))] pub struct OneofObjectField { pub ident: Ident, pub attrs: Vec, pub fields: Fields, #[darling(default)] pub name: Option, #[darling(default)] pub validator: Option, #[darling(default)] pub visible: Option, #[darling(default)] pub secret: bool, } #[derive(FromDeriveInput)] #[darling(attributes(graphql), forward_attrs(doc))] pub struct OneofObject { pub ident: Ident, pub generics: Generics, pub attrs: Vec, pub data: Data, #[darling(default)] pub internal: bool, #[darling(default)] pub name: Option, #[darling(default)] pub rename_fields: Option, #[darling(default)] pub visible: Option, #[darling(default, multiple, rename = "concrete")] pub concretes: Vec, } #[derive(FromMeta)] pub struct InterfaceFieldArgument { pub name: String, #[darling(default)] pub desc: Option, #[darling(rename = "type")] pub ty: LitStr, #[darling(default)] pub default: Option, #[darling(default)] pub default_with: Option, #[darling(default)] pub visible: Option, #[darling(default)] pub secret: bool, } #[derive(FromMeta)] pub struct InterfaceField { pub name: SpannedValue, #[darling(rename = "type")] pub ty: LitStr, #[darling(default)] pub method: Option, #[darling(default)] pub desc: Option, #[darling(default, multiple, rename = "arg")] pub args: Vec, #[darling(default)] pub deprecation: Deprecation, #[darling(default)] pub external: bool, #[darling(default)] pub provides: Option, #[darling(default)] pub requires: Option, #[darling(default)] pub visible: Option, #[darling(default)] pub oneof: bool, } #[derive(FromVariant)] pub struct InterfaceMember { pub ident: Ident, pub fields: Fields, } #[derive(FromDeriveInput)] #[darling(attributes(graphql), forward_attrs(doc))] pub struct Interface { pub ident: Ident, pub generics: Generics, pub attrs: Vec, pub data: Data, #[darling(default)] pub internal: bool, #[darling(default)] pub name: Option, #[darling(default)] pub rename_fields: Option, #[darling(default)] pub rename_args: Option, #[darling(default, multiple, rename = "field")] pub fields: Vec, #[darling(default)] pub extends: bool, #[darling(default)] pub visible: Option, } #[derive(FromMeta, Default)] #[darling(default)] pub struct Scalar { pub internal: bool, pub name: Option, pub use_type_description: bool, pub visible: Option, pub specified_by_url: Option, } #[derive(FromMeta, Default)] #[darling(default)] pub struct Subscription { pub internal: bool, pub name: Option, pub rename_fields: Option, pub rename_args: Option, pub use_type_description: bool, pub extends: bool, pub visible: Option, } #[derive(FromMeta, Default)] #[darling(default)] pub struct SubscriptionFieldArgument { pub name: Option, pub desc: Option, pub default: Option, pub default_with: Option, pub validator: Option, pub visible: Option, pub secret: bool, } #[derive(FromMeta, Default)] #[darling(default)] pub struct SubscriptionField { pub skip: bool, pub name: Option, pub deprecation: Deprecation, pub guard: Option>, pub visible: Option, pub complexity: Option, pub oneof: bool, } #[derive(FromField)] pub struct MergedObjectField { pub ident: Option, pub ty: Type, } #[derive(FromDeriveInput)] #[darling(attributes(graphql), forward_attrs(doc))] pub struct MergedObject { pub ident: Ident, pub generics: Generics, pub attrs: Vec, pub data: Data, #[darling(default)] pub internal: bool, #[darling(default)] pub name: Option, #[darling(default)] pub cache_control: CacheControl, #[darling(default)] pub extends: bool, #[darling(default)] pub visible: Option, #[darling(default)] pub serial: bool, } #[derive(FromField)] pub struct MergedSubscriptionField { pub ident: Option, pub ty: Type, } #[derive(FromDeriveInput)] #[darling(attributes(graphql), forward_attrs(doc))] pub struct MergedSubscription { pub ident: Ident, pub generics: Generics, pub attrs: Vec, pub data: Data, #[darling(default)] pub internal: bool, #[darling(default)] pub name: Option, #[darling(default)] pub visible: Option, #[darling(default)] pub extends: bool, } #[derive(Debug, Copy, Clone, FromMeta)] pub enum RenameRule { #[darling(rename = "lowercase")] Lower, #[darling(rename = "UPPERCASE")] Upper, #[darling(rename = "PascalCase")] Pascal, #[darling(rename = "camelCase")] Camel, #[darling(rename = "snake_case")] Snake, #[darling(rename = "SCREAMING_SNAKE_CASE")] ScreamingSnake, } impl RenameRule { fn rename(&self, name: impl AsRef) -> String { match self { Self::Lower => name.as_ref().to_lowercase(), Self::Upper => name.as_ref().to_uppercase(), Self::Pascal => name.as_ref().to_pascal_case(), Self::Camel => name.as_ref().to_camel_case(), Self::Snake => name.as_ref().to_snake_case(), Self::ScreamingSnake => name.as_ref().to_screaming_snake_case(), } } } #[derive(Debug, Copy, Clone)] pub enum RenameTarget { Type, EnumItem, Field, Argument, } impl RenameTarget { fn rule(&self) -> RenameRule { match self { RenameTarget::Type => RenameRule::Pascal, RenameTarget::EnumItem => RenameRule::ScreamingSnake, RenameTarget::Field => RenameRule::Camel, RenameTarget::Argument => RenameRule::Camel, } } pub fn rename(&self, name: impl AsRef) -> String { self.rule().rename(name) } } pub trait RenameRuleExt { fn rename(&self, name: impl AsRef, target: RenameTarget) -> String; } impl RenameRuleExt for Option { fn rename(&self, name: impl AsRef, target: RenameTarget) -> String { self.unwrap_or(target.rule()).rename(name) } } #[derive(FromDeriveInput)] #[darling(forward_attrs(doc))] pub struct Description { pub ident: Ident, pub generics: Generics, pub attrs: Vec, #[darling(default)] pub internal: bool, } #[derive(Debug)] pub enum NewTypeName { New(String), Rust, Original, } impl Default for NewTypeName { fn default() -> Self { Self::Original } } impl FromMeta for NewTypeName { fn from_word() -> darling::Result { Ok(Self::Rust) } fn from_string(value: &str) -> darling::Result { Ok(Self::New(value.to_string())) } fn from_bool(value: bool) -> darling::Result { if value { Ok(Self::Rust) } else { Ok(Self::Original) } } } #[derive(FromDeriveInput)] #[darling(attributes(graphql), forward_attrs(doc))] pub struct NewType { pub ident: Ident, pub generics: Generics, pub attrs: Vec, pub data: Data, #[darling(default)] pub internal: bool, #[darling(default)] pub name: NewTypeName, #[darling(default)] pub visible: Option, #[darling(default)] pub specified_by_url: Option, } #[derive(FromMeta, Default)] #[darling(default)] pub struct ComplexObject { pub internal: bool, pub name: Option, pub rename_fields: Option, pub rename_args: Option, } #[derive(FromMeta, Default)] #[darling(default)] pub struct ComplexObjectField { pub skip: bool, pub name: Option, pub deprecation: Deprecation, pub cache_control: CacheControl, pub external: bool, pub provides: Option, pub requires: Option, pub guard: Option>, pub visible: Option, pub complexity: Option, #[darling(multiple)] pub derived: Vec, pub flatten: bool, pub oneof: bool, } #[derive(FromMeta, Default)] #[darling(default)] pub struct Directive { pub internal: bool, pub name: Option, pub visible: Option, pub repeatable: bool, pub rename_args: Option, #[darling(multiple, rename = "location")] pub locations: Vec, } #[derive(Debug, Copy, Clone, FromMeta)] #[darling(rename_all = "lowercase")] pub enum DirectiveLocation { Field, } impl Display for DirectiveLocation { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { DirectiveLocation::Field => write!(f, "FIELD"), } } }