wasm4-impl 0.1.3

Procedural macros implementation for WASM-4 bindings
Documentation
use super::{parse, Sprite};
use proc_macro2::TokenStream as TokenStream2;
use quote::{quote, quote_spanned, ToTokens};
use std::path::PathBuf;
use wasm4_common::draw::BitsPerPixel;

pub struct Output {
    pub package_name: syn::Ident,
    pub palette: (
        wasm4_common::draw::Palette,
        parse::ItemConst<parse::CommonPaletteExpr>,
    ),
    pub sprites: Vec<(Sprite, parse::ItemConst<parse::IncludeSpriteExpr>)>,
}

impl ToTokens for Output {
    fn to_tokens(&self, tokens: &mut TokenStream2) {
        let Output {
            package_name,
            palette: (palette, palette_item),
            sprites,
        } = self;

        {
            let parse::ItemConst::<parse::CommonPaletteExpr> {
                attrs,
                vis,
                const_token,
                ident,
                colon_token,
                ty,
                eq_token,
                expr,
                semi_token,
            } = palette_item;

            let ty = quote_spanned! { ty.spans[0] => #package_name::draw::Palette };
            let color_ty = quote! { #package_name::draw::Color };
            let palette = palette.map(|c| c.0);
            let expr = quote_spanned! { expr.name_token.span => [ #( #color_ty(#palette) ),* ] };

            attrs.iter().for_each(|attr| attr.to_tokens(tokens));
            vis.to_tokens(tokens);
            const_token.to_tokens(tokens);
            ident.to_tokens(tokens);
            colon_token.to_tokens(tokens);
            ty.to_tokens(tokens);
            eq_token.to_tokens(tokens);
            expr.to_tokens(tokens);
            semi_token.to_tokens(tokens);
        }

        let pkg_root = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
        for (sprite, sprite_item) in sprites {
            let parse::ItemConst::<parse::IncludeSpriteExpr> {
                attrs,
                vis,
                const_token,
                ident,
                colon_token,
                ty,
                eq_token,
                expr,
                semi_token,
            } = sprite_item;

            let bytes = sprite.bytes();
            let shape = sprite.shape();
            let bpp = match sprite.bpp() {
                BitsPerPixel::One => quote! { #package_name::draw::BitsPerPixel::One },
                BitsPerPixel::Two => quote! { #package_name::draw::BitsPerPixel::Two },
            };
            let indices = sprite.indices.into_u16();
            let byte_len = bytes.len();
            let indices_ty = quote! { #package_name::draw::DrawIndices };
            let ty = quote_spanned! { ty.spans[0] => #package_name::draw::Sprite<[u8; #byte_len]> };
            let path = format!("{}", pkg_root.join(expr.path.value()).display());
            let path = quote_spanned!(expr.path.span() => #path);
            let expr = quote_spanned! { expr.name_token.span =>
                match #package_name::draw::Sprite::from_byte_array(
                    [ #( #bytes ),* ],
                    [ #( #shape ),* ],
                    #bpp,
                    unsafe { <#indices_ty>::from_u16_unchecked(#indices) },
                ) {
                    Some(s) => s,
                    None => panic!(),
                }
            };

            attrs.iter().for_each(|attr| attr.to_tokens(tokens));
            vis.to_tokens(tokens);
            const_token.to_tokens(tokens);
            ident.to_tokens(tokens);
            colon_token.to_tokens(tokens);
            ty.to_tokens(tokens);
            eq_token.to_tokens(tokens);
            expr.to_tokens(tokens);
            semi_token.to_tokens(tokens);
            quote! { const _: &[u8] = include_bytes!(#path); }.to_tokens(tokens);
        }
    }
}