rtlola-interpreter-macros 0.2.0

Derive macros to help with integrating the rtlola interpreter
Documentation
use convert_case::{Case, Casing};
use proc_macro2::Ident;
use quote::format_ident;
use syn::{GenericArgument, PathArguments, Type};

pub(crate) fn new_snake_ident(base: &Ident, suffix: &str) -> Ident {
    let ident_str = base.to_string().to_case(Case::Snake);
    format_ident!("{}_{}", ident_str, suffix, span = base.span())
}

pub(crate) fn option_inner_type(ty: &Type) -> Option<&GenericArgument> {
    if let Type::Path(path) = ty {
        let segment = path.path.segments.last()?;
        if &segment.ident.to_string() == "Option" {
            if let PathArguments::AngleBracketed(args) = &segment.arguments {
                return args.args.first();
            }
        }
    };
    None
}

pub(crate) fn hashmap_types(ty: &Type) -> Option<(Vec<&Type>, &GenericArgument)> {
    if let Type::Path(path) = ty {
        let segment = path.path.segments.last()?;
        if &segment.ident.to_string() == "HashMap" {
            if let PathArguments::AngleBracketed(args) = &segment.arguments {
                let value_ty = args.args.last()?;
                let instance = args.args.first()?;
                let args: Vec<&Type> = if let GenericArgument::Type(Type::Tuple(args)) = &instance {
                    args.elems.iter().collect()
                } else if let GenericArgument::Type(t) = instance {
                    vec![t]
                } else {
                    vec![]
                };
                return Some((args, value_ty));
            }
        }
    };
    None
}

pub(crate) fn hashset_types(ty: &Type) -> Option<Vec<&Type>> {
    if let Type::Path(path) = ty {
        let segment = path.path.segments.last()?;
        if &segment.ident.to_string() == "HashSet" {
            if let PathArguments::AngleBracketed(args) = &segment.arguments {
                let instance = args.args.first()?;
                let args: Vec<&Type> = if let GenericArgument::Type(Type::Tuple(args)) = &instance {
                    args.elems.iter().collect()
                } else if let GenericArgument::Type(t) = instance {
                    vec![t]
                } else {
                    vec![]
                };
                return Some(args);
            }
        }
    };
    None
}

pub(crate) fn is_bool(ty: &Type) -> bool {
    if let Type::Path(path) = ty {
        path.path
            .segments
            .last()
            .map(|segment| segment.ident == "bool")
            .unwrap_or(false)
    } else {
        false
    }
}

pub(crate) fn is_bool_generic(ty: &GenericArgument) -> bool {
    if let GenericArgument::Type(ty) = ty {
        is_bool(ty)
    } else {
        false
    }
}