use crate::utils::parse_value; use graphql_parser::query::Value; use syn::{Attribute, AttributeArgs, Error, Meta, MetaList, NestedMeta, Result, Type}; #[derive(Debug)] pub struct Object { pub internal: bool, pub name: Option, pub desc: Option, pub fields: Vec, } impl Object { pub fn parse(args: AttributeArgs) -> Result { let mut internal = false; let mut name = None; let mut desc = None; let mut fields = Vec::new(); for arg in args { match arg { 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.", )); } } } NestedMeta::Meta(Meta::List(ls)) if ls.path.is_ident("field") => { fields.push(Field::parse(&ls)?); } _ => {} } } Ok(Self { internal, name, desc, fields, }) } } #[derive(Debug)] pub struct Argument { pub name: String, pub desc: Option, pub ty: Type, pub default: Option, } #[derive(Debug)] pub struct Field { pub name: String, pub resolver: Option, pub desc: Option, pub ty: Type, pub is_owned: bool, pub arguments: Vec, pub deprecation: Option, } impl Field { fn parse(ls: &MetaList) -> Result { let mut name = None; let mut resolver = None; let mut desc = None; let mut ty = None; let mut is_owned = false; let mut arguments = Vec::new(); let mut deprecation = None; for meta in &ls.nested { match meta { NestedMeta::Meta(Meta::Path(p)) if p.is_ident("owned") => { is_owned = 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.", )); } } else if nv.path.is_ident("resolver") { if let syn::Lit::Str(lit) = &nv.lit { resolver = Some(lit.value()); } else { return Err(Error::new_spanned( &nv.lit, "Attribute 'resolver' should be a string.", )); } } else if nv.path.is_ident("type") { if let syn::Lit::Str(lit) = &nv.lit { if let Ok(ty2) = syn::parse_str::(&lit.value()) { ty = Some(ty2); } else { return Err(Error::new_spanned(&lit, "Expect type")); } desc = Some(lit.value()); } else { return Err(Error::new_spanned( &nv.lit, "Attribute 'type' should be a string.", )); } } else if nv.path.is_ident("deprecation") { if let syn::Lit::Str(lit) = &nv.lit { deprecation = Some(lit.value()); } else { return Err(Error::new_spanned( &nv.lit, "Attribute 'deprecation' should be a string.", )); } } } NestedMeta::Meta(Meta::List(ls)) => { if ls.path.is_ident("arg") { let mut name = None; let mut desc = None; let mut ty = None; let mut default = None; for meta in &ls.nested { if let NestedMeta::Meta(Meta::NameValue(nv)) = meta { 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.", )); } } else if nv.path.is_ident("default") { if let syn::Lit::Str(lit) = &nv.lit { match parse_value(&lit.value()) { Ok(Value::Variable(_)) => { return Err(Error::new_spanned( &nv.lit, "The default cannot be a variable", )) } Ok(value) => default = Some(value), Err(err) => { return Err(Error::new_spanned( &nv.lit, format!("Invalid value: {}", err), )); } } } else { return Err(Error::new_spanned( &nv.lit, "Attribute 'default' should be a string.", )); } } else if nv.path.is_ident("type") { if let syn::Lit::Str(lit) = &nv.lit { if let Ok(ty2) = syn::parse_str::(&lit.value()) { ty = Some(ty2); } else { return Err(Error::new_spanned(&lit, "expect type")); } } else { return Err(Error::new_spanned( &nv.lit, "Attribute 'type' should be a string.", )); } } } } if name.is_none() { return Err(Error::new_spanned(ls, "missing name.")); } if ty.is_none() { return Err(Error::new_spanned(ls, "missing type.")); } arguments.push(Argument { name: name.unwrap(), desc, ty: ty.unwrap(), default, }); } } _ => {} } } if name.is_none() { return Err(Error::new_spanned(ls, "missing name.")); } if ty.is_none() { return Err(Error::new_spanned(ls, "missing type.")); } Ok(Self { name: name.unwrap(), resolver, desc, ty: ty.unwrap(), is_owned, arguments, deprecation, }) } } #[derive(Debug)] pub struct Enum { pub internal: bool, pub name: Option, pub desc: Option, } impl Enum { pub fn parse(args: AttributeArgs) -> Result { 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::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, name, desc, }) } } #[derive(Debug)] pub struct EnumItem { pub name: Option, pub desc: Option, pub deprecation: Option, } impl EnumItem { pub fn parse(attrs: &[Attribute]) -> Result { let mut name = None; let mut desc = None; let mut deprecation = None; for attr in attrs { if attr.path.is_ident("item") { if let Ok(Meta::List(args)) = attr.parse_meta() { for meta in args.nested { match meta { 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.", )); } } else if nv.path.is_ident("deprecation") { if let syn::Lit::Str(lit) = nv.lit { deprecation = Some(lit.value()); } else { return Err(Error::new_spanned( &nv.lit, "Attribute 'deprecation' should be a string.", )); } } } _ => {} } } } } } Ok(Self { name, desc, deprecation, }) } } #[derive(Debug)] pub struct InputField { pub internal: bool, pub name: Option, pub desc: Option, pub default: Option, } impl InputField { pub fn parse(attrs: &[Attribute]) -> Result { let mut internal = false; let mut name = None; let mut desc = None; let mut default = None; for attr in attrs { if attr.path.is_ident("field") { if let Ok(Meta::List(args)) = attr.parse_meta() { for meta in args.nested { match meta { 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.", )); } } else if nv.path.is_ident("default") { if let syn::Lit::Str(lit) = nv.lit { match parse_value(&lit.value()) { Ok(Value::Variable(_)) => { return Err(Error::new_spanned( &lit, "The default cannot be a variable", )) } Ok(value) => default = Some(value), Err(err) => { return Err(Error::new_spanned( &lit, format!("Invalid value: {}", err), )); } } } else { return Err(Error::new_spanned( &nv.lit, "Attribute 'default' should be a string.", )); } } } _ => {} } } } } } Ok(Self { internal, name, desc, default, }) } } #[derive(Debug)] pub struct InputObject { pub internal: bool, pub name: Option, pub desc: Option, } impl InputObject { pub fn parse(args: AttributeArgs) -> Result { 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::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, name, desc, }) } }