instrs-derive 0.1.0

Derive macro for instrs crate
Documentation
use proc_macro2::TokenStream;
use quote_into::quote_into;
use syn::*;

use crate::util::*;
use crate::info::*;

pub(crate) fn impl_into_bytes(input: &Info, s: &mut TokenStream) {
    fn foreach_field_into_bytes(fields: &Fields, s: &mut TokenStream) {
        foreach_field(fields, s, |_, ident, s| {
            quote_into!{s +=
                Serialize::into_bytes::<S>(#(ident), f)?;
            }
        })
    }

    fn instruction_matches(input: &Info, s: &mut TokenStream) {
        for (i, instruction) in input.instructions.iter().enumerate() {
            let i = (&input.repr)(i);

            quote_into!{s += 
                #(input.name)::#(instruction.name) #{match_fields(&instruction.fields, s)} => {
                    f.push(#i);
                    #{foreach_field_into_bytes(&instruction.fields, s)}
                },
            };
        }
    }

    quote_into!{ s +=
        fn into_bytes<S: instrs::Size>(&self, f: &mut Vec<u8>) -> Result<(), instrs::Error> {
            match self {
                #{instruction_matches(input, s)}
            }

            Ok(())
        }
    };
}

pub(crate) fn impl_from_bytes(input: &Info, s: &mut TokenStream) {
    fn foreach_field_from_string(fields: &Fields, s: &mut TokenStream) {
        foreach_field(fields, s, |_, ident, s| {
            quote_into!{s +=
                let #(ident) = Serialize::from_bytes::<S>(f)?;
            }
        })
    }

    fn instruction_matches(input: &Info, s: &mut TokenStream) {
        for (i, instruction) in input.instructions.iter().enumerate() {
            let i = (&input.repr)(i);

            quote_into!{ s+=
                Some(#i) => {
                    *f = &f[1..];
                    #{foreach_field_from_string(&instruction.fields, s)}
                    
                    return Ok(Self::#(instruction.name) #{match_fields(&instruction.fields, s)})
                }
            };
        }
    }

    let n_instructions = input.instructions.len() as u32 - 1;

    quote_into!{ s +=
        fn from_bytes<S: instrs::Size>(f: &mut &[u8]) -> Result<Self, instrs::Error> {
            match f.first() {
                #{instruction_matches(&input, s)}

                None => return Err(instrs::Error::ExpectedBytes(1)),
                _ => return Err(Error::ExpectedRange(0..=#(n_instructions)))
            }
        }
    };
}