Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[codegen] Add wrappers on enums for shared methods #760

Merged
merged 1 commit into from
Jan 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions font-codegen/src/fields.rs
Original file line number Diff line number Diff line change
Expand Up @@ -685,21 +685,24 @@ impl Field {
type_tokens
}

pub(crate) fn table_getter(&self, generic: Option<&syn::Ident>) -> Option<TokenStream> {
pub(crate) fn table_getter_return_type(&self) -> Option<TokenStream> {
if !self.has_getter() {
return None;
}

let return_type = self.raw_getter_return_type();
if self.is_version_dependent() {
Some(quote!(Option<#return_type>))
} else {
Some(return_type)
}
}
pub(crate) fn table_getter(&self, generic: Option<&syn::Ident>) -> Option<TokenStream> {
let return_type = self.table_getter_return_type()?;
let name = &self.name;
let is_array = self.is_array();
let is_var_array = self.is_var_array();
let is_versioned = self.is_version_dependent();

let mut return_type = self.raw_getter_return_type();
if is_versioned {
return_type = quote!(Option<#return_type>);
}

let range_stmt = self.getter_range_stmt();
let mut read_stmt = if let Some(args) = &self.attrs.read_with_args {
let get_args = args.to_tokens_for_table_getter();
Expand Down
2 changes: 1 addition & 1 deletion font-codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ pub(crate) fn generate_parse_module(items: &Items) -> Result<proc_macro2::TokenS
Item::Record(item) => record::generate(item, items)?,
Item::Table(item) => table::generate(item)?,
Item::GenericGroup(item) => table::generate_group(item)?,
Item::Format(item) => table::generate_format_group(item)?,
Item::Format(item) => table::generate_format_group(item, items)?,
Item::RawEnum(item) => flags_enums::generate_raw_enum(item),
Item::Flags(item) => flags_enums::generate_flags(item),
Item::Extern(..) => Default::default(),
Expand Down
23 changes: 20 additions & 3 deletions font-codegen/src/parsing.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//! raw parsing code

use std::{backtrace::Backtrace, collections::HashMap, fmt::Display, ops::Deref, str::FromStr};
use std::{
backtrace::Backtrace, collections::HashMap, fmt::Display, hash::Hash, ops::Deref, str::FromStr,
};

use font_types::Tag;
use indexmap::IndexMap;
Expand Down Expand Up @@ -326,7 +328,7 @@ impl InlineExpr {
}
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) enum FieldType {
Offset {
typ: syn::Ident,
Expand All @@ -352,7 +354,7 @@ pub(crate) enum FieldType {
VarLenArray(CustomArray),
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) enum OffsetTarget {
Table(syn::Ident),
Array(Box<FieldType>),
Expand All @@ -366,6 +368,21 @@ pub(crate) struct CustomArray {
lifetime: Option<syn::Lifetime>,
}

impl PartialEq for CustomArray {
fn eq(&self, other: &Self) -> bool {
self.inner == other.inner && self.lifetime == other.lifetime
}
}

impl Eq for CustomArray {}

impl Hash for CustomArray {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.inner.hash(state);
self.lifetime.hash(state);
}
}

impl CustomArray {
pub(crate) fn compile_type(&self) -> TokenStream {
let inner = &self.inner;
Expand Down
82 changes: 81 additions & 1 deletion font-codegen/src/table.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
//! codegen for table objects

use std::collections::HashMap;

use crate::{fields::FieldConstructorInfo, parsing::logged_syn_error};
use indexmap::IndexMap;
use proc_macro2::{Span, TokenStream};
use quote::{quote, ToTokens};

Expand Down Expand Up @@ -593,6 +596,72 @@ fn generate_format_constructors(item: &TableFormat, items: &Items) -> syn::Resul
})
}

fn generate_format_shared_getters(item: &TableFormat, items: &Items) -> syn::Result<TokenStream> {
// okay so we want to identify the getters that exist on all variants.
let all_variants = item
.variants
.iter()
.map(|var| {
let type_name = var.type_name();
match items.get(type_name) {
Some(Item::Table(item)) => Ok(item),
_ => Err(logged_syn_error(
type_name.span(),
"must be a table defined in this file",
)),
}
})
.collect::<Result<Vec<_>, _>>()?;
// okay so now we have all of the actual inner types, and we need to find which
// getters are shared between all of them
let mut field_counts = IndexMap::new();
let mut all_fields = HashMap::new();
for table in &all_variants {
for field in table.fields.iter().filter(|fld| fld.has_getter()) {
let key = (&field.name, &field.typ);
// we have to convert the tokens to a string to get hash/ord/etc
*field_counts.entry(key).or_insert(0usize) += 1;
all_fields.entry(&field.name).or_insert(field);
}
}

let shared_fields = field_counts
.into_iter()
.filter(|(_, count)| *count == all_variants.len())
.map(|((name, _), _)| all_fields.get(name).unwrap())
.collect::<Vec<_>>();

let getters = shared_fields
.iter()
.map(|fld| generate_format_getter_for_shared_field(item, fld));

// now we have a collection of fields present on all variants, and
// we need to actually generate the wrapping getter

Ok(quote!( #( #getters )* ))
}

fn generate_format_getter_for_shared_field(item: &TableFormat, field: &Field) -> TokenStream {
let docs = &field.attrs.docs;
let method_name = &field.name;
let return_type = field.table_getter_return_type();
let arms = item.variants.iter().map(|variant| {
let var_name = &variant.name;
quote!(Self::#var_name(item) => item.#method_name(), )
});

// but we also need to handle offset getters, and that's a pain

quote! {
#( #docs )*
pub fn #method_name(&self) -> #return_type {
match self {
#( #arms )*
}
}
}
}

fn generate_format_from_obj(
item: &TableFormat,
parse_module: &syn::Path,
Expand Down Expand Up @@ -628,7 +697,7 @@ fn generate_format_from_obj(
})
}

pub(crate) fn generate_format_group(item: &TableFormat) -> syn::Result<TokenStream> {
pub(crate) fn generate_format_group(item: &TableFormat, items: &Items) -> syn::Result<TokenStream> {
let name = &item.name;
let docs = &item.attrs.docs;
let variants = item
Expand Down Expand Up @@ -684,13 +753,24 @@ pub(crate) fn generate_format_group(item: &TableFormat) -> syn::Result<TokenStre
.map(|lit| lit.base10_parse::<usize>().unwrap())
.unwrap_or(0);

let getters = generate_format_shared_getters(item, items)?;
let getters = (!getters.is_empty()).then(|| {
quote! {
impl<'a> #name<'a> {
#getters
}
}
});

Ok(quote! {
#( #docs )*
#[derive(Clone)]
pub enum #name<'a> {
#( #variants ),*
}

#getters

impl<'a> FontRead<'a> for #name<'a> {
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
let format: #format = data.read_at(#format_offset)?;
Expand Down
20 changes: 20 additions & 0 deletions read-fonts/generated/generated_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -932,6 +932,26 @@ pub enum BaseCoord<'a> {
Format3(BaseCoordFormat3<'a>),
}

impl<'a> BaseCoord<'a> {
/// Format identifier — format = 1
pub fn base_coord_format(&self) -> u16 {
match self {
Self::Format1(item) => item.base_coord_format(),
Self::Format2(item) => item.base_coord_format(),
Self::Format3(item) => item.base_coord_format(),
}
}

/// X or Y value, in design units
pub fn coordinate(&self) -> i16 {
match self {
Self::Format1(item) => item.coordinate(),
Self::Format2(item) => item.coordinate(),
Self::Format3(item) => item.coordinate(),
}
}
}

impl<'a> FontRead<'a> for BaseCoord<'a> {
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
let format: u16 = data.read_at(0usize)?;
Expand Down
35 changes: 35 additions & 0 deletions read-fonts/generated/generated_bitmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -851,6 +851,41 @@ pub enum IndexSubtable<'a> {
Format5(IndexSubtable5<'a>),
}

impl<'a> IndexSubtable<'a> {
/// Format of this IndexSubTable.
pub fn index_format(&self) -> u16 {
match self {
Self::Format1(item) => item.index_format(),
Self::Format2(item) => item.index_format(),
Self::Format3(item) => item.index_format(),
Self::Format4(item) => item.index_format(),
Self::Format5(item) => item.index_format(),
}
}

/// Format of EBDT image data.
pub fn image_format(&self) -> u16 {
match self {
Self::Format1(item) => item.image_format(),
Self::Format2(item) => item.image_format(),
Self::Format3(item) => item.image_format(),
Self::Format4(item) => item.image_format(),
Self::Format5(item) => item.image_format(),
}
}

/// Offset to image data in EBDT table.
pub fn image_data_offset(&self) -> u32 {
match self {
Self::Format1(item) => item.image_data_offset(),
Self::Format2(item) => item.image_data_offset(),
Self::Format3(item) => item.image_data_offset(),
Self::Format4(item) => item.image_data_offset(),
Self::Format5(item) => item.image_data_offset(),
}
}
}

impl<'a> FontRead<'a> for IndexSubtable<'a> {
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
let format: u16 = data.read_at(0usize)?;
Expand Down
17 changes: 17 additions & 0 deletions read-fonts/generated/generated_cmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,23 @@ pub enum CmapSubtable<'a> {
Format14(Cmap14<'a>),
}

impl<'a> CmapSubtable<'a> {
/// Format number is set to 0.
pub fn format(&self) -> u16 {
match self {
Self::Format0(item) => item.format(),
Self::Format2(item) => item.format(),
Self::Format4(item) => item.format(),
Self::Format6(item) => item.format(),
Self::Format8(item) => item.format(),
Self::Format10(item) => item.format(),
Self::Format12(item) => item.format(),
Self::Format13(item) => item.format(),
Self::Format14(item) => item.format(),
}
}
}

impl<'a> FontRead<'a> for CmapSubtable<'a> {
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
let format: u16 = data.read_at(0usize)?;
Expand Down
82 changes: 82 additions & 0 deletions read-fonts/generated/generated_colr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -785,6 +785,48 @@ pub enum ClipBox<'a> {
Format2(ClipBoxFormat2<'a>),
}

impl<'a> ClipBox<'a> {
/// Set to 1.
pub fn format(&self) -> u8 {
match self {
Self::Format1(item) => item.format(),
Self::Format2(item) => item.format(),
}
}

/// Minimum x of clip box.
pub fn x_min(&self) -> FWord {
match self {
Self::Format1(item) => item.x_min(),
Self::Format2(item) => item.x_min(),
}
}

/// Minimum y of clip box.
pub fn y_min(&self) -> FWord {
match self {
Self::Format1(item) => item.y_min(),
Self::Format2(item) => item.y_min(),
}
}

/// Maximum x of clip box.
pub fn x_max(&self) -> FWord {
match self {
Self::Format1(item) => item.x_max(),
Self::Format2(item) => item.x_max(),
}
}

/// Maximum y of clip box.
pub fn y_max(&self) -> FWord {
match self {
Self::Format1(item) => item.y_max(),
Self::Format2(item) => item.y_max(),
}
}
}

impl<'a> FontRead<'a> for ClipBox<'a> {
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
let format: u8 = data.read_at(0usize)?;
Expand Down Expand Up @@ -1525,6 +1567,46 @@ pub enum Paint<'a> {
Composite(PaintComposite<'a>),
}

impl<'a> Paint<'a> {
/// Set to 1.
pub fn format(&self) -> u8 {
match self {
Self::ColrLayers(item) => item.format(),
Self::Solid(item) => item.format(),
Self::VarSolid(item) => item.format(),
Self::LinearGradient(item) => item.format(),
Self::VarLinearGradient(item) => item.format(),
Self::RadialGradient(item) => item.format(),
Self::VarRadialGradient(item) => item.format(),
Self::SweepGradient(item) => item.format(),
Self::VarSweepGradient(item) => item.format(),
Self::Glyph(item) => item.format(),
Self::ColrGlyph(item) => item.format(),
Self::Transform(item) => item.format(),
Self::VarTransform(item) => item.format(),
Self::Translate(item) => item.format(),
Self::VarTranslate(item) => item.format(),
Self::Scale(item) => item.format(),
Self::VarScale(item) => item.format(),
Self::ScaleAroundCenter(item) => item.format(),
Self::VarScaleAroundCenter(item) => item.format(),
Self::ScaleUniform(item) => item.format(),
Self::VarScaleUniform(item) => item.format(),
Self::ScaleUniformAroundCenter(item) => item.format(),
Self::VarScaleUniformAroundCenter(item) => item.format(),
Self::Rotate(item) => item.format(),
Self::VarRotate(item) => item.format(),
Self::RotateAroundCenter(item) => item.format(),
Self::VarRotateAroundCenter(item) => item.format(),
Self::Skew(item) => item.format(),
Self::VarSkew(item) => item.format(),
Self::SkewAroundCenter(item) => item.format(),
Self::VarSkewAroundCenter(item) => item.format(),
Self::Composite(item) => item.format(),
}
}
}

impl<'a> FontRead<'a> for Paint<'a> {
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
let format: u8 = data.read_at(0usize)?;
Expand Down
Loading