spez-macros 0.1.0

proc_macro crate for spez
Documentation
use std::iter::once;
use std::iter::FromIterator;
use syn::punctuated::Punctuated;
use syn::Token;

pub struct Args {
	pub for_token: Token![for],
	pub param: Option<syn::Ident>,
	pub at_token: Option<Token![=]>,
	pub expr: syn::Expr,
	pub semicolon_token: Token![;],
	pub arms: Vec<Arm>,
}

pub struct Arm {
	pub match_token: Token![match],
	pub generics: syn::Generics,
	pub ty: syn::Type,
	pub arrow_token: Option<Token![->]>,
	pub return_type: Option<syn::Type>,
	pub body: syn::Block,
}

impl syn::parse::Parse for Args {
	fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
		let for_token = input.parse()?;
		let param;
		let at_token;
		let expr;
		if input.peek2(Token![=]) {
			param = Some(input.parse()?);
			at_token = Some(input.parse()?);
			expr = input.parse()?;
		} else if input.peek(syn::Ident) && input.peek2(Token![;]) {
			let ident: syn::Ident = input.parse()?;
			param = Some(ident.clone());
			at_token = None;
			expr = ident_to_expr(ident);
		} else {
			param = None;
			at_token = None;
			expr = input.parse()?;
		}
		Ok(Self {
			for_token,
			param,
			at_token,
			expr,
			semicolon_token: input.parse()?,
			arms: {
				let mut arms = Vec::new();
				while !input.is_empty() {
					arms.push(input.parse()?);
				}
				arms
			},
		})
	}
}

impl syn::parse::Parse for Arm {
	fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
		let match_token = input.parse()?;
		let generics = if input.peek(Token![<]) {
			input.parse()?
		} else {
			syn::Generics::default()
		};
		let ty = input.parse()?;
		let where_clause: Option<syn::WhereClause> = input.parse()?;
		let (arrow_token, return_type) = if input.peek(Token![->]) {
			(Some(input.parse()?), Some(input.parse()?))
		} else {
			(None, None)
		};
		let body = input.parse()?;
		Ok(Self {
			match_token,
			generics: syn::Generics {
				where_clause,
				..generics
			},
			ty,
			arrow_token,
			return_type,
			body,
		})
	}
}

fn ident_to_expr(ident: syn::Ident) -> syn::Expr {
	syn::Expr::Path(syn::ExprPath {
		attrs: Vec::new(),
		qself: None,
		path: syn::Path {
			leading_colon: None,
			segments: Punctuated::from_iter(once(syn::PathSegment {
				ident,
				arguments: syn::PathArguments::None,
			})),
		},
	})
}