use crate::from_bit_stream::{derive_enum_from_bit_stream, derive_struct_from_bit_stream};
use crate::optional_segment_parser::derive_optional_segment_parser;
use crate::struct_attr::{GPPStructHelperAttribute, GPPStructKind};
use proc_macro::TokenStream;
use proc_macro2::Ident;
use quote::{TokenStreamExt, quote};
use syn::{Attribute, Data, DataStruct, DeriveInput, parse_macro_input};
mod enum_variant_attr;
mod field_attr;
mod from_bit_stream;
mod optional_segment_parser;
mod struct_attr;
#[proc_macro_derive(FromBitStream, attributes(gpp))]
pub fn derive_from_bit_stream(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
match input.data {
Data::Struct(s) => {
let attr =
GPPStructHelperAttribute::new(&input.attrs).expect("attribute parsing failed");
derive_struct_from_bit_stream(&s, &input.ident, &attr).into()
}
Data::Enum(e) => {
derive_enum_from_bit_stream(&e, &input.ident).into()
}
_ => TokenStream::new(),
}
}
#[proc_macro_derive(GPPSection, attributes(gpp))]
pub fn derive_gpp_section(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let ident = input.ident;
if let Data::Struct(s) = input.data {
let stream = quote! {
impl crate::sections::DecodableSection for #ident {
const ID: crate::sections::SectionId = crate::sections::SectionId::#ident;
}
};
let attr = GPPStructHelperAttribute::new(&input.attrs).expect("attribute parsing failed");
match attr.kind {
GPPStructKind::Base64Data => {
impl_base64_gpp_section(ident, s, &attr, stream)
}
GPPStructKind::WithOptionalSegments(_) => {
impl_segmented_gpp_section(ident, s, &attr, stream)
}
}
} else {
TokenStream::new()
}
}
fn impl_base64_gpp_section(
ident: Ident,
s: DataStruct,
attr: &GPPStructHelperAttribute,
mut stream: proc_macro2::TokenStream,
) -> TokenStream {
stream.append_all(quote! {
impl ::std::str::FromStr for #ident {
type Err = crate::sections::SectionDecodeError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
use bitstream_io::BitRead;
crate::core::base64_bit_reader(s.as_bytes()).parse()
}
}
});
stream.append_all(derive_struct_from_bit_stream(&s, &ident, attr));
stream.into()
}
fn impl_segmented_gpp_section(
ident: Ident,
s: DataStruct,
attr: &GPPStructHelperAttribute,
mut stream: proc_macro2::TokenStream,
) -> TokenStream {
stream.append_all(quote! {
impl ::std::str::FromStr for #ident {
type Err = crate::sections::SectionDecodeError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
use crate::sections::SegmentedStr;
s.parse_segmented_str()
}
}
});
stream.append_all(derive_struct_from_bit_stream(&s, &ident, attr));
stream.append_all(derive_optional_segment_parser(&s, &ident, attr));
stream.into()
}
fn find_gpp_attr(attrs: &[Attribute]) -> Option<&Attribute> {
attrs.iter().find(|attr| attr.path().is_ident("gpp"))
}