use syn::spanned::Spanned as _;
#[derive(Debug)]
pub struct ApiDefinitions {
pub apis: Vec<ApiDefinition>,
}
#[derive(Debug)]
pub struct ApiDefinition {
pub visibility: syn::Visibility,
pub name: syn::Ident,
pub generics: syn::Generics,
pub definitions: Vec<ApiMethod>,
}
#[derive(Debug)]
pub struct ApiMethod {
pub signature: syn::Signature,
pub attributes: ApiMethodAttrs,
}
#[derive(Debug, Default)]
pub struct ApiMethodAttrs {
pub method: Option<String>,
pub positional_params: bool,
}
impl ApiMethod {
pub fn is_void_ret_type(&self) -> bool {
let ret_ty = match &self.signature.output {
syn::ReturnType::Default => return true,
syn::ReturnType::Type(_, ty) => ty,
};
let tuple_ret_ty = match &**ret_ty {
syn::Type::Tuple(tuple) => tuple,
_ => return false,
};
tuple_ret_ty.elems.is_empty()
}
}
#[derive(Debug)]
struct ApiMethods {
definitions: Vec<ApiMethod>,
}
enum ApiMethodAttr {
Method(syn::LitStr),
PositionalParams,
}
impl syn::parse::Parse for ApiDefinitions {
fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
let mut out = ApiDefinitions { apis: Vec::new() };
while !input.is_empty() {
out.apis.push(input.parse()?);
}
Ok(out)
}
}
impl syn::parse::Parse for ApiDefinition {
fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
let visibility = input.parse()?;
let name = input.parse()?;
let generics = input.parse()?;
let group: proc_macro2::Group = input.parse()?;
assert_eq!(group.delimiter(), proc_macro2::Delimiter::Brace);
let defs: ApiMethods = syn::parse2(group.stream())?;
Ok(ApiDefinition {
visibility,
name,
generics,
definitions: defs.definitions,
})
}
}
impl syn::parse::Parse for ApiMethod {
fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
let item: syn::TraitItemMethod = input.parse()?;
if item.default.is_some() {
return Err(syn::Error::new(item.default.span(),
"It is forbidden to provide a default implementation for methods in the API definition"));
}
let mut attributes = ApiMethodAttrs::default();
for attribute in &item.attrs {
if attribute.path.is_ident("rpc") {
let attrs = attribute.parse_args()?;
attributes.try_merge(attrs)?;
} else {
}
}
Ok(ApiMethod {
signature: item.sig,
attributes,
})
}
}
impl ApiMethodAttrs {
fn try_merge(&mut self, other: ApiMethodAttrs) -> syn::parse::Result<()> {
if let Some(method) = other.method {
if self.method.is_some() {
}
self.method = Some(method);
}
if other.positional_params {
self.positional_params = true;
}
Ok(())
}
}
impl syn::parse::Parse for ApiMethodAttrs {
fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
let mut out = ApiMethodAttrs::default();
let list = input
.parse_terminated::<_, syn::token::Comma>(|input| input.parse::<ApiMethodAttr>())?;
for attr in list {
match attr {
ApiMethodAttr::Method(method) => {
if out.method.is_some() {
return Err(syn::Error::new(
method.span(),
"Duplicate method attribute found",
));
}
out.method = Some(method.value());
}
ApiMethodAttr::PositionalParams => out.positional_params = true,
}
}
Ok(out)
}
}
impl syn::parse::Parse for ApiMethodAttr {
fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
let attr: syn::Ident = input.parse()?;
if attr == "method" {
let _: syn::token::Eq = input.parse()?;
let val = input.parse()?;
Ok(ApiMethodAttr::Method(val))
} else if attr == "positional_params" {
Ok(ApiMethodAttr::PositionalParams)
} else {
Err(syn::Error::new(
attr.span(),
&format!("Unknown attribute: {}", attr.to_string()),
))
}
}
}
impl syn::parse::Parse for ApiMethods {
fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
let mut out = ApiMethods {
definitions: Vec::new(),
};
while !input.is_empty() {
let method: ApiMethod = input.parse()?;
out.definitions.push(method);
}
Ok(out)
}
}