use syn::{
braced, parenthesized,
parse::{Parse, ParseStream},
Block, Expr, Ident, Result, Token, Type,
};
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum FieldMode {
Ref,
Mut,
Copy,
}
#[derive(Clone, Debug)]
pub struct FieldParam {
pub mode: FieldMode,
pub name: Ident,
}
impl Parse for FieldParam {
fn parse(input: ParseStream) -> Result<Self> {
if input.peek(Token![&]) {
input.parse::<Token![&]>()?;
}
let mode = if input.peek(Token![mut]) {
input.parse::<Token![mut]>()?;
FieldMode::Mut
} else if input.peek(Ident) {
let fork = input.fork();
let ident: Ident = fork.parse()?;
if ident == "copy" {
input.parse::<Ident>()?;
FieldMode::Copy
} else {
FieldMode::Ref
}
} else {
FieldMode::Ref
};
let name = input.parse()?;
Ok(FieldParam { mode, name })
}
}
#[derive(Clone)]
pub struct ExtraParam {
pub name: Ident,
}
impl Parse for ExtraParam {
fn parse(input: ParseStream) -> Result<Self> {
let name = input.parse()?;
input.parse::<Token![:]>()?;
let _ty: Type = input.parse()?;
Ok(ExtraParam { name })
}
}
pub struct MethodDef {
pub name: Ident,
pub field_params: Vec<FieldParam>,
pub extra_params: Vec<ExtraParam>,
pub body: Block,
}
impl Parse for MethodDef {
fn parse(input: ParseStream) -> Result<Self> {
let name: Ident = input.parse()?;
let content;
parenthesized!(content in input);
let mut field_params = Vec::new();
let mut extra_params = Vec::new();
while !content.is_empty() {
if is_extra_param(&content) {
extra_params.push(content.parse()?);
} else {
field_params.push(content.parse()?);
}
if content.peek(Token![,]) {
content.parse::<Token![,]>()?;
}
}
if input.peek(Token![->]) {
input.parse::<Token![->]>()?;
let _return_type: Type = input.parse()?;
}
let body = input.parse()?;
Ok(MethodDef {
name,
field_params,
extra_params,
body,
})
}
}
fn is_extra_param(input: ParseStream) -> bool {
let fork = input.fork();
if fork.peek(Token![&]) {
let _ = fork.parse::<Token![&]>();
}
if fork.peek(Token![mut]) {
let _ = fork.parse::<Token![mut]>();
}
if fork.parse::<Ident>().is_err() {
return false;
}
fork.peek(Token![:])
}
#[derive(Debug)]
pub struct FieldDef {
pub mode: FieldMode,
pub name: Ident,
}
impl Parse for FieldDef {
fn parse(input: ParseStream) -> Result<Self> {
let mode = if input.peek(Token![mut]) {
input.parse::<Token![mut]>()?;
FieldMode::Mut
} else if input.peek(Ident) {
let fork = input.fork();
let ident: Ident = fork.parse()?;
if ident == "copy" {
input.parse::<Ident>()?;
FieldMode::Copy
} else {
FieldMode::Ref
}
} else {
FieldMode::Ref
};
let name = input.parse()?;
Ok(FieldDef { mode, name })
}
}
pub struct WithInput {
pub receiver: Expr,
pub methods: Vec<MethodDef>,
pub fields: Vec<FieldDef>,
pub body: Block,
}
impl Parse for WithInput {
fn parse(input: ParseStream) -> Result<Self> {
let receiver = input.parse()?;
input.parse::<Token![,]>()?;
let main_content;
braced!(main_content in input);
let mut methods = Vec::new();
let mut fields = Vec::new();
let mut body = None;
while !main_content.is_empty() {
let keyword: Ident = main_content.parse()?;
let keyword_str = keyword.to_string();
match keyword_str.as_str() {
"methods" => {
let methods_content;
braced!(methods_content in main_content);
while !methods_content.is_empty() {
methods.push(methods_content.parse()?);
}
}
"fields" => {
let fields_content;
braced!(fields_content in main_content);
while !fields_content.is_empty() {
fields.push(fields_content.parse()?);
}
}
"body" => {
body = Some(main_content.parse()?);
}
_ => {
return Err(syn::Error::new(
keyword.span(),
format!("expected 'methods', 'fields', or 'body', found '{}'", keyword_str),
));
}
}
}
let body = body.ok_or_else(|| {
syn::Error::new(input.span(), "missing 'body { }' block")
})?;
Ok(WithInput {
receiver,
methods,
fields,
body,
})
}
}