lol-macros 0.0.1

macros for lol
Documentation
//! consts
use crate::util::*;
use proc_macro::TokenStream;
use proc_macro2::Span;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use quote::ToTokens;
use syn::meta::ParseNestedMeta;
use syn::parse::Parse;
use syn::punctuated::Punctuated;
use syn::{Ident, Token};

#[derive(Default)]
struct Attrs {
    crate_name: Option<syn::Path>,
    from: Option<(Ident, Vec<Ident>)>,
}

fn parse_attr_from(meta: &ParseNestedMeta) -> syn::Result<syn::Ident> {
    let _token = meta.input.parse::<Token![=]>()?;
    let lit: syn::LitStr = meta.input.parse()?;
    Ok(syn::Ident::new(lit.value().as_str(), Span::call_site()))
}

fn parse_attr_field(meta: &ParseNestedMeta) -> syn::Result<Vec<Ident>> {
    let _token = meta.input.parse::<Token![=]>()?;
    let lit: syn::LitStr = meta.input.parse()?;
    let segments = lit
        .value()
        .as_str()
        .split('.')
        .map(|s| Ident::new(s, Span::call_site()))
        .collect();
    Ok(segments)
}

fn fold_attrs(mut state: Attrs, attr: &syn::Attribute) -> syn::Result<Attrs> {
    if attr.path().is_ident("lol") {
        attr.parse_nested_meta(|meta| {
            if meta.path.is_ident("crate") {
                let value: syn::Path = meta.value().and_then(|value| value.parse())?;
                state.crate_name = Some(value);
                Ok(())
            } else if meta.path.is_ident("from") {
                let from = parse_attr_from(&meta)?;
                let _comma = meta.input.parse::<Token![,]>()?;
                let attr: syn::Ident = meta.input.parse()?;
                if attr == "field" {
                    let field = parse_attr_field(&meta)?;
                    state.from = Some((from, field));
                    Ok(())
                } else {
                    Err(meta.error("Expected \"field\" attribute"))
                }
            } else {
                Err(meta.error("Unexpected \"lol-io\" attributes"))
            }
        })?;
        Ok(state)
    } else {
        Ok(state)
    }
}

enum ConstAssignmentValue {
    Lit(syn::LitInt),
    Path(syn::Path),
}

impl Parse for ConstAssignmentValue {
    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
        let lookahead = input.lookahead1();
        if lookahead.peek(syn::LitInt) {
            input.parse().map(ConstAssignmentValue::Lit)
        } else if lookahead.peek(syn::Ident) {
            input.parse().map(ConstAssignmentValue::Path)
        } else {
            Err(lookahead.error())
        }
    }
}

/// ConstAssignment
///
/// Shorthand assignments of a const member of a tuple struct. IE:
/// const ARCH = 0;
struct ConstAssignment {
    visibility: Option<syn::Visibility>,
    const_token: Token![const],
    name: syn::Ident,
    eq: Token![=],
    value: ConstAssignmentValue,
}

impl ToTokens for ConstAssignment {
    fn to_tokens(&self, tokens: &mut TokenStream2) {
        let vis = &self.visibility;
        let const_token = self.const_token;
        let name = &self.name;
        let eq = self.eq;
        let value = match &self.value {
            ConstAssignmentValue::Lit(l) => l.into_token_stream(),
            ConstAssignmentValue::Path(p) => p.into_token_stream(),
        };
        quote! {#vis #const_token #name: Self #eq Self(#value);}.to_tokens(tokens);
    }
}

impl Parse for ConstAssignment {
    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
        Ok(ConstAssignment {
            visibility: input.parse().map(Some).unwrap_or(None),
            const_token: input.parse()?,
            name: input.parse()?,
            eq: input.parse()?,
            value: input.parse()?,
        })
    }
}

/// Const
///
/// Abbrieviated struct definition for a tuple struct which wraps an integer
/// IE: pub struct ProcessorArchitecture: u32 { ... }
struct Const {
    attrs: Vec<syn::Attribute>,
    visibility: Option<syn::Visibility>,
    struct_token: Token![struct],
    name: syn::Ident,
    _colon_token: Token![:],
    ty: syn::Type,
    _brace_token: syn::token::Brace,
    assignments: Punctuated<ConstAssignment, Token![;]>,
}

impl ToTokens for Const {
    fn to_tokens(&self, tokens: &mut TokenStream2) {
        let attrs = &self.attrs;
        let visibility = &self.visibility;
        let struct_token = self.struct_token;
        let name = &self.name;
        let ty = &self.ty;
        let assignments = &self.assignments;
        let assignment_tokens = assignments
            .into_iter()
            .map(|assignment| quote! {#assignment});
        quote! {
            #(#attrs)*
            #[repr(transparent)]
            #[derive(Clone, Copy, Default, Eq, PartialEq, Hash)]
            #visibility #struct_token #name(#ty);
            impl #name {
                #(#assignment_tokens)*
                pub fn raw(&self) -> #ty {
                    self.0
                }
                /// Safety: Be sure the value has meaning for this type.
                pub const unsafe fn from_raw(raw: #ty) -> Self {
                    Self(raw)
                }
            }
            impl AsRef<#ty> for #name {
                fn as_ref(&self) -> &#ty {
                    &self.0
                }
            }
        }
        .to_tokens(tokens);
    }
}

impl Parse for Const {
    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
        let content;
        let attrs = input.call(syn::Attribute::parse_outer)?;
        Ok(Const {
            attrs,
            visibility: input.parse().map(Some).unwrap_or(None),
            struct_token: input.parse()?,
            name: input.parse()?,
            _colon_token: input.parse()?,
            ty: input.parse()?,
            _brace_token: syn::braced!(content in input),
            assignments: content.parse_terminated(ConstAssignment::parse, Token![;])?,
        })
    }
}

pub struct Items(pub Vec<TokenStream2>);
impl Parse for Items {
    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
        let mut tokens = Vec::new();
        while !input.is_empty() {
            tokens.push(input.parse::<Const>()?.into_token_stream());
        }
        Ok(Items(tokens))
    }
}

pub(crate) fn make_expanded(toks: TokenStream) -> syn::Result<TokenStream2> {
    let items: Items = syn::parse(toks)?;
    Ok(items.0.into_iter().collect())
}

// TODO Add NativeBitConst/NativeBitFlags
pub(crate) fn derive_native_bit_flag(toks: TokenStream) -> syn::Result<TokenStream2> {
    let input = syn::parse::<syn::DeriveInput>(toks)?;
    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
    let name = &input.ident;
    let attrs = input.attrs.iter().try_fold(Attrs::default(), fold_attrs)?;
    let crate_name = attrs.crate_name.unwrap_or_else(resolve_crate);
    let from = attrs.from.map(|(from, member)| {
        quote! {
            impl #impl_generics #name #ty_generics #where_clause {
                pub fn parse(other: &#from) -> Self {
                    /// Safety: It is expected that the Windows OS will put a
                    /// meaning value in here. It is expected that "other" has
                    /// already been populated by the Windows system.
                    unsafe { Self::from_raw(other.#(#member).*) }
                }
            }
        }
    });

    // BitAnd &
    let impl_bitand = quote! {
        impl #impl_generics std::ops::BitAnd #ty_generics #where_clause for #name {
            type Output = #name;
            fn bitand(self, rhs: Self) -> Self::Output {
                Self(self.0 & rhs.0)
            }
        }
    };

    // BitAndAssign &=
    let impl_bitand_assign = quote! {
        impl #impl_generics std::ops::BitAndAssign #ty_generics #where_clause for #name {
            fn bitand_assign(&mut self, rhs: Self) {
                *self = Self(self.0 & rhs.0)
            }
        }
    };

    // BitOr |
    let impl_bitor = quote! {
        impl #impl_generics std::ops::BitOr #ty_generics #where_clause for #name {
            type Output = #name;
            fn bitor(self, rhs: Self) -> Self::Output {
                Self(self.0 | rhs.0)
            }
        }
    };

    // BitOrAssign |=
    let impl_bitor_assign = quote! {
        impl #impl_generics std::ops::BitOrAssign #ty_generics #where_clause for #name {
            fn bitor_assign(&mut self, rhs: Self) {
                *self = Self(self.0 | rhs.0)
            }
        }
    };

    // BitXor ^
    let impl_bitxor = quote! {
        impl #impl_generics std::ops::BitXor #ty_generics #where_clause for #name {
            type Output = #name;
            fn bitxor(self, rhs: Self) -> Self::Output {
                Self(self.0 ^ rhs.0)
            }
        }
    };

    // BitXorAssign ^=
    let impl_bitxor_assign = quote! {
        impl #impl_generics std::ops::BitXorAssign #ty_generics #where_clause for #name {
            fn bitxor_assign(&mut self, rhs: Self) {
                *self = Self(self.0 ^ rhs.0)
            }
        }
    };

    // Not
    let impl_not = quote! {
        impl #impl_generics std::ops::Not #ty_generics #where_clause for #name {
            type Output = #name;
            fn not(self) -> Self::Output {
                Self(!self.0)
            }
        }
    };
    Ok(quote! {
        impl #impl_generics #crate_name::kernel::co::NativeBitFlags for #name #ty_generics #where_clause {
        }
        #from
        #impl_bitand
        #impl_bitand_assign
        #impl_bitor
        #impl_bitor_assign
        #impl_bitxor
        #impl_bitxor_assign
        #impl_not
    })
}