plux-codegen 0.3.0

A code generator for plux-rs
Documentation
use proc_macro2::{Ident, Span};
use syn::{Error, FnArg, Pat, Result, Type, TypePath};

pub(crate) fn get_literal_type(ty: &Type) -> &TypePath {
    match ty {
        Type::Path(path) => path,
        Type::Reference(r) => match &*r.elem {
            Type::Path(path) => path,
            _ => panic!("Wrong type"),
        },
        _ => panic!("Wrong type"),
    }
}

pub(crate) fn get_externals(arg: &FnArg) -> Vec<(Ident, &Type)> {
    match arg {
        FnArg::Receiver(_) => panic!("Receiver is not supported"),
        FnArg::Typed(pat_type) => match &*pat_type.pat {
            Pat::Tuple(pat_tuple) => match &*pat_type.ty {
                Type::Tuple(ty_tuple) => pat_tuple
                    .elems
                    .iter()
                    .zip(ty_tuple.elems.iter())
                    .filter_map(|(pat, ty)| match ty {
                        Type::Reference(r) => {
                            pat_to_ident(pat).unwrap().map(|name| (name, &*r.elem))
                        }
                        _ => panic!("type must contain only references (&T)"),
                    })
                    .collect(),
                _ => panic!("Wrong type"),
            },
            pat => match pat_to_ident(pat).unwrap() {
                Some(name) => match &*pat_type.ty {
                    Type::Reference(r) => vec![(name, &*r.elem)],
                    _ => panic!("type must contain only references (&T)"),
                },
                None => vec![],
            },
        },
    }
}

pub(crate) fn get_inputs<'a, I>(args: I) -> Vec<(Ident, &'a Type)>
where
    I: Iterator<Item = &'a FnArg>,
{
    args.map(|arg| match arg {
        FnArg::Receiver(_) => panic!("Receiver is not supported"),
        FnArg::Typed(pat) => (
            pat_to_ident(&*pat.pat)
                .unwrap()
                .unwrap_or(Ident::new("arg", Span::call_site())),
            pat.ty.as_ref(),
        ),
    })
    .collect()
}

pub(crate) fn pat_to_ident(pat: &Pat) -> Result<Option<Ident>> {
    match pat {
        Pat::Const(_) => Ok(None),
        Pat::Ident(pat) => Ok(Some(pat.ident.clone())),
        Pat::Lit(_) => Ok(None),
        Pat::Macro(_) => Ok(None),
        Pat::Or(_) => Ok(None),
        Pat::Paren(_) => Ok(None),
        Pat::Path(pat) => Ok(Some(pat.path.get_ident().unwrap().clone())),
        Pat::Range(_) => Ok(None),
        Pat::Reference(pat) => pat_to_ident(&pat.pat),
        Pat::Rest(_) => Ok(None),
        Pat::Type(pat) => pat_to_ident(&pat.pat),
        Pat::Wild(_) => Ok(None),
        _ => Err(Error::new_spanned(pat, "Wrong type")),
    }
}

pub(crate) fn clear_ref(ty: &Type) -> TypePath {
    match ty {
        Type::Path(path) => {
            let mut path = path.clone();
            match &mut path.path.segments.last_mut().unwrap().arguments {
                syn::PathArguments::AngleBracketed(args) => {
                    args.args.iter_mut().for_each(|arg| match arg {
                        syn::GenericArgument::Type(ty) => {
                            *arg = syn::GenericArgument::Type(Type::Path(clear_ref(ty)))
                        }
                        _ => panic!("Wrong type"),
                    });
                    path
                }
                _ => panic!("Wrong type"),
            }
        }
        Type::Reference(r) => match &*r.elem {
            Type::Path(path) => path.clone(),
            _ => panic!("Wrong type"),
        },
        _ => panic!("Wrong type"),
    }
}