packed_struct_codegen 0.10.1

This crate implements the code generation for the packed_struct library.
Documentation
use crate::pack::*;
use crate::common::*;
use proc_macro2::Span;
use quote::{ToTokens};
use syn::parse_quote;
use crate::utils::*;

pub fn struct_runtime_formatter(parsed: &PackStruct) -> syn::Result<proc_macro2::TokenStream> {
    let (impl_generics, ty_generics, where_clause) = parsed.derive_input.generics.split_for_impl();
    let name = &parsed.derive_input.ident;
    let snake_name = to_snake_case(&name.to_string());
    let stdlib_prefix = collections_prefix();
    let debug_fields_fn = syn::Ident::new(&format!("debug_fields_{}", snake_name), Span::call_site());

    let display_header = format!("{} ({} {})",
        name,
        parsed.num_bytes,
        if parsed.num_bytes == 1 { "byte" } else { "bytes" }
    );
    
    let mut debug_fields = vec![];
    for field in &parsed.fields {
        match field {
            FieldKind::Regular { ref ident, ref field } => {
                let name_str = &ident.to_string();
                let bits: syn::ExprRange = syn::parse_str(&format!("{}..{}", field.bit_range.start, field.bit_range.end))?;
                
                debug_fields.push(quote! {
                    ::packed_struct::debug_fmt::DebugBitField {
                        name: #name_str.into(),
                        bits: #bits,
                        display_value: format!("{:?}", src.#ident).into()
                    }
                });
            },
            FieldKind::Array { ref ident, ref elements, .. } => {
                for (i, field) in elements.iter().enumerate() {
                    let name_str = format!("{}[{}]", ident, i);
                    let bits: syn::ExprRange = syn::parse_str(&format!("{}..{}", field.bit_range.start, field.bit_range.end))?;
                    
                    debug_fields.push(quote! {
                        ::packed_struct::debug_fmt::DebugBitField {
                            name: #name_str.into(),
                            bits: #bits,
                            display_value: format!("{:?}", src.#ident[#i]).into()
                        }
                    });
                }
                
            }
        }
    }

    let num_fields = debug_fields.len();
    let num_bytes = parsed.num_bytes;
    let result_ty = result_type();

    let q = quote! {
        #[doc(hidden)]
        pub fn #debug_fields_fn(src: &#name) -> [::packed_struct::debug_fmt::DebugBitField<'static>; #num_fields] {
            [#(#debug_fields),*]
        }

        #[allow(unused_imports)]
        impl #impl_generics ::packed_struct::debug_fmt::PackedStructDebug for #name #ty_generics #where_clause {
            fn fmt_fields(&self, fmt: &mut #stdlib_prefix::fmt::Formatter) -> #result_ty <(), #stdlib_prefix::fmt::Error> {
                use ::packed_struct::PackedStruct;
                
                let fields = #debug_fields_fn(self);
                let packed: [u8; #num_bytes] = self.pack()?;
                ::packed_struct::debug_fmt::packable_fmt_fields(fmt, &packed, &fields)
            }

            fn packed_struct_display_header() -> &'static str {
                #display_header
            }
        }

        #[allow(unused_imports)]
        impl #impl_generics #stdlib_prefix::fmt::Display for #name #ty_generics #where_clause {
            #[allow(unused_imports)]
            fn fmt(&self, f: &mut #stdlib_prefix::fmt::Formatter) -> #stdlib_prefix::fmt::Result {                
                let display = ::packed_struct::debug_fmt::PackedStructDisplay::new(self);
                display.fmt(f)
            }
        }
    };
    
    Ok(q)
}

use std::ops::Range;

use crate::utils_syn::tokens_to_string;


pub fn type_docs(parsed: &PackStruct) -> proc_macro2::TokenStream {
    let mut doc = quote! {};

    let mut doc_html = |s: &str| {        
        let p: syn::Attribute = parse_quote! {
            #[doc = #s ]
        };

        p.to_tokens(&mut doc);
    };

    doc_html(&format!("Structure that can be packed an unpacked into {size_bytes} bytes.\r\n",
        size_bytes = parsed.num_bytes
    ));

    doc_html("<table>\r\n");
    doc_html("<thead><tr><td>Bit, MSB0</td><td>Name</td><td>Type</td></tr></thead>\r\n");
    doc_html("<tbody>\r\n");

    {
        let mut emit_field_docs = |bits: &Range<usize>, field_ident, ty| {

            let bits_str = {
                if bits.start == bits.end {
                    format!("{}", bits.start)
                } else {
                    format!("{}:{}", bits.start, bits.end)
                }
            };

            // todo: friendly integer, reserved types. add LSB/MSB integer info.

            doc_html(&format!("<tr><td>{}</td><td>{}</td><td>{}</td></tr>\r\n", bits_str, field_ident, tokens_to_string(ty)));
        };

        for field in &parsed.fields {
            match field {
                FieldKind::Regular { ref ident, ref field } => {
                    emit_field_docs(&field.bit_range, ident.to_string(), &field.ty);
                },
                FieldKind::Array { ref ident, ref elements, .. } => {
                    for (i, field) in elements.iter().enumerate() {
                        emit_field_docs(&field.bit_range, format!("{}[{}]", ident, i), &field.ty);
                    }
                }
            }            
        }
    }


    doc_html("</tbody>\r\n");
    doc_html("</table>\r\n");

    doc
}