blob: 9c3f53a5f90c854ef370c05657d233ba3bf0539c [file] [edit]
//! codegen for generic group types
use proc_macro2::TokenStream;
use quote::quote;
use crate::parsing::GenericGroup;
pub(crate) fn generate(item: &GenericGroup) -> syn::Result<TokenStream> {
let docs = &item.attrs.docs;
let name = &item.name;
let inner = &item.inner_type;
let mut variant_decls = Vec::new();
let mut read_match_arms = Vec::new();
let mut dyn_inner_arms = Vec::new();
let mut of_unit_arms = Vec::new();
for var in &item.variants {
let var_name = &var.name;
let type_id = &var.type_id;
let typ = &var.typ;
variant_decls.push(quote! { #var_name ( #inner <'a, #typ<'a>> ) });
read_match_arms
.push(quote! { #type_id => Ok(#name :: #var_name (FontRead::read(bytes)?)) });
dyn_inner_arms.push(quote! { #name :: #var_name(table) => table });
of_unit_arms.push(quote! { #name :: #var_name(inner) => inner.of_unit_type() });
}
let first_var_name = &item.variants.first().unwrap().name;
let of_unit_docs = &[
" Return the inner table, removing the specific generics.",
"",
" This lets us return a single concrete type we can call methods on.",
];
Ok(quote! {
#( #docs)*
pub enum #name <'a> {
#( #variant_decls, )*
}
impl Default for #name<'_> {
fn default() -> Self {
Self::#first_var_name(Default::default())
}
}
impl ReadArgs for #name<'_> {
type Args = ();
}
impl<'a> FontRead<'a> for #name <'a> {
fn read_with_args(bytes: FontData<'a>, _: ()) -> Result<Self, ReadError> {
let discriminant = #inner::read_discriminant(bytes)?;
match discriminant {
#( #read_match_arms, )*
other => Err(ReadError::InvalidFormat(other.into())),
}
}
}
impl<'a> #name <'a> {
#[allow(dead_code)]
#( #[doc = #of_unit_docs] )*
pub(crate) fn of_unit_type(&self) -> #inner<'a, ()> {
match self {
#( #of_unit_arms, )*
}
}
}
#[cfg(feature = "experimental_traverse")]
impl<'a> #name <'a> {
fn dyn_inner(&self) -> &(dyn SomeTable<'a> + 'a) {
match self {
#( #dyn_inner_arms, )*
}
}
}
#[cfg(feature = "experimental_traverse")]
impl<'a> SomeTable<'a> for #name <'a> {
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
self.dyn_inner().get_field(idx)
}
fn type_name(&self) -> &str {
self.dyn_inner().type_name()
}
}
#[cfg(feature = "experimental_traverse")]
impl std::fmt::Debug for #name<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.dyn_inner().fmt(f)
}
}
})
}
pub(crate) fn generate_compile(
item: &GenericGroup,
parse_module: &syn::Path,
) -> syn::Result<TokenStream> {
let docs = &item.attrs.docs;
let name = &item.name;
let inner = &item.inner_type;
let mut variant_decls = Vec::new();
let mut write_match_arms = Vec::new();
let mut validate_match_arms = Vec::new();
let mut from_obj_match_arms = Vec::new();
let mut type_arms = Vec::new();
let mut from_impls = Vec::new();
let from_type = quote!(#parse_module :: #name);
for var in &item.variants {
let var_name = &var.name;
let typ = &var.typ;
variant_decls.push(quote! { #var_name ( #inner <#typ> ) });
write_match_arms.push(quote! { Self :: #var_name (table) => table.write_into(writer) });
validate_match_arms.push(quote! { Self :: #var_name(table) => table.validate_impl(ctx) });
from_obj_match_arms.push(
quote! { #from_type :: #var_name(table) => Self :: #var_name(table.to_owned_obj(data)) },
);
type_arms.push(quote! { Self:: #var_name(table) => table.table_type() });
from_impls.push(quote! {
impl From<#inner <#typ>> for #name {
fn from(src: #inner <#typ>) -> #name {
#name :: #var_name ( src )
}
}
});
}
let first_var_name = &item.variants.first().unwrap().name;
Ok(quote! {
#( #docs)*
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum #name {
#( #variant_decls, )*
}
impl Default for #name {
fn default() -> Self {
Self::#first_var_name(Default::default())
}
}
impl FontWrite for #name {
fn write_into(&self, writer: &mut TableWriter) {
match self {
#( #write_match_arms, )*
}
}
fn table_type(&self) -> TableType {
match self {
#( #type_arms, )*
}
}
}
impl Validate for #name {
fn validate_impl(&self, ctx: &mut ValidationCtx) {
match self {
#( #validate_match_arms, )*
}
}
}
impl FromObjRef< #from_type :: <'_>> for #name {
fn from_obj_ref(from: & #from_type :: <'_>, data: FontData) -> Self {
match from {
#( #from_obj_match_arms, )*
}
}
}
impl FromTableRef< #from_type <'_>> for #name {}
#( #from_impls )*
})
}