use std::{fmt::Debug, ops::Deref};
use types::{
BigEndian, F2Dot14, FWord, Fixed, GlyphId, LongDateTime, MajorMinor, NameId, Nullable,
Offset16, Offset24, Offset32, Scalar, Tag, UfWord, Uint24, Version16Dot16,
};
use crate::{
array::{ComputedArray, VarLenArray},
read::{ComputeSize, ReadArgs},
FontData, FontRead, FontReadWithArgs, ReadError, VarSize,
};
pub enum FieldType<'a> {
I8(i8),
U8(u8),
I16(i16),
U16(u16),
I32(i32),
U32(u32),
U24(Uint24),
Tag(Tag),
FWord(FWord),
UfWord(UfWord),
MajorMinor(MajorMinor),
Version16Dot16(Version16Dot16),
F2Dot14(F2Dot14),
Fixed(Fixed),
LongDateTime(LongDateTime),
GlyphId(GlyphId),
NameId(NameId),
BareOffset(OffsetType),
ResolvedOffset(ResolvedOffset<'a>),
StringOffset(StringOffset<'a>),
ArrayOffset(ArrayOffset<'a>),
Record(RecordResolver<'a>),
Array(Box<dyn SomeArray<'a> + 'a>),
Unknown,
}
#[derive(Clone, Copy)]
pub enum OffsetType {
Offset16(u16),
Offset24(Uint24),
Offset32(u32),
}
impl OffsetType {
pub fn to_u32(self) -> u32 {
match self {
Self::Offset16(val) => val.into(),
Self::Offset24(val) => val.into(),
Self::Offset32(val) => val,
}
}
}
pub struct ResolvedOffset<'a> {
pub offset: OffsetType,
pub target: Result<Box<dyn SomeTable<'a> + 'a>, ReadError>,
}
pub struct StringOffset<'a> {
pub offset: OffsetType,
pub target: Result<Box<dyn SomeString<'a> + 'a>, ReadError>,
}
pub struct ArrayOffset<'a> {
pub offset: OffsetType,
pub target: Result<Box<dyn SomeArray<'a> + 'a>, ReadError>,
}
pub(crate) struct ArrayOfOffsets<'a, O> {
type_name: &'static str,
offsets: &'a [O],
resolver: Box<dyn Fn(&O) -> FieldType<'a> + 'a>,
}
impl<'a, O> SomeArray<'a> for ArrayOfOffsets<'a, O> {
fn type_name(&self) -> &str {
self.type_name
}
fn len(&self) -> usize {
self.offsets.len()
}
fn get(&self, idx: usize) -> Option<FieldType<'a>> {
let off = self.offsets.get(idx)?;
let target = (self.resolver)(off);
Some(target)
}
}
impl<'a> FieldType<'a> {
pub fn array_of_records<T>(
type_name: &'static str,
records: &'a [T],
data: FontData<'a>,
) -> FieldType<'a>
where
T: Clone + SomeRecord<'a> + 'a,
{
ArrayOfRecords {
type_name,
data,
records,
}
.into()
}
pub fn computed_array<T>(
type_name: &'static str,
array: ComputedArray<'a, T>,
data: FontData<'a>,
) -> FieldType<'a>
where
T: FontReadWithArgs<'a> + ComputeSize + SomeRecord<'a> + 'a,
T::Args: Copy + 'static,
{
ComputedArrayOfRecords {
type_name,
data,
array,
}
.into()
}
pub fn var_array<T>(
type_name: &'static str,
array: VarLenArray<'a, T>,
data: FontData<'a>,
) -> FieldType<'a>
where
T: FontRead<'a> + VarSize + SomeRecord<'a> + 'a,
{
VarLenArrayOfRecords {
type_name,
data,
array,
}
.into()
}
pub fn array_of_offsets<O>(
type_name: &'static str,
offsets: &'a [O],
resolver: impl Fn(&O) -> FieldType<'a> + 'a,
) -> Self
where {
FieldType::Array(Box::new(ArrayOfOffsets {
type_name,
offsets,
resolver: Box::new(resolver),
}))
}
pub fn offset_to_array_of_scalars<T: SomeArray<'a> + 'a>(
offset: impl Into<OffsetType>,
result: impl Into<Option<Result<T, ReadError>>>,
) -> Self {
let offset = offset.into();
match result.into() {
Some(target) => FieldType::ArrayOffset(ArrayOffset {
offset,
target: target.map(|x| Box::new(x) as Box<dyn SomeArray>),
}),
None => FieldType::BareOffset(offset),
}
}
pub fn offset_to_array_of_records<T: Clone + SomeRecord<'a> + 'a>(
offset: impl Into<OffsetType>,
result: impl Into<Option<Result<&'a [T], ReadError>>>,
type_name: &'static str,
data: FontData<'a>,
) -> Self {
let offset = offset.into();
match result.into() {
Some(target) => {
let target = target.map(|records| {
Box::new(ArrayOfRecords {
type_name,
data,
records,
}) as Box<dyn SomeArray>
});
FieldType::ArrayOffset(ArrayOffset { offset, target })
}
None => FieldType::BareOffset(offset),
}
}
pub fn offset<T: SomeTable<'a> + 'a>(
offset: impl Into<OffsetType>,
result: impl Into<Option<Result<T, ReadError>>>,
) -> Self {
let offset = offset.into();
match result.into() {
Some(target) => FieldType::ResolvedOffset(ResolvedOffset {
offset,
target: target.map(|x| Box::new(x) as Box<dyn SomeTable>),
}),
None => FieldType::BareOffset(offset),
}
}
pub fn unknown_offset(offset: impl Into<OffsetType>) -> Self {
Self::BareOffset(offset.into())
}
}
pub struct Field<'a> {
pub name: &'static str,
pub value: FieldType<'a>,
}
pub trait SomeTable<'a> {
fn type_name(&self) -> &str;
fn get_field(&self, idx: usize) -> Option<Field<'a>>;
}
impl<'a> dyn SomeTable<'a> + 'a {
pub fn iter(&self) -> impl Iterator<Item = Field<'a>> + '_ {
FieldIter {
table: self,
idx: 0,
}
}
}
struct FieldIter<'a, 'b> {
table: &'b dyn SomeTable<'a>,
idx: usize,
}
impl<'a, 'b> Iterator for FieldIter<'a, 'b> {
type Item = Field<'a>;
fn next(&mut self) -> Option<Self::Item> {
let this = self.idx;
self.idx += 1;
self.table.get_field(this)
}
}
impl<'a> SomeTable<'a> for Box<dyn SomeTable<'a> + 'a> {
fn type_name(&self) -> &str {
self.deref().type_name()
}
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
self.deref().get_field(idx)
}
}
pub trait SomeRecord<'a> {
fn traverse(self, data: FontData<'a>) -> RecordResolver<'a>;
}
pub struct RecordResolver<'a> {
pub(crate) name: &'static str,
pub(crate) get_field: Box<dyn Fn(usize, FontData<'a>) -> Option<Field<'a>> + 'a>,
pub(crate) data: FontData<'a>,
}
pub trait SomeArray<'a> {
fn type_name(&self) -> &str;
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
fn get(&self, idx: usize) -> Option<FieldType<'a>>;
}
impl<'a> dyn SomeArray<'a> + 'a {
pub fn iter(&self) -> impl Iterator<Item = FieldType<'a>> + '_ {
ArrayIter {
array: self,
idx: 0,
}
}
}
struct ArrayIter<'a, 'b> {
array: &'b dyn SomeArray<'a>,
idx: usize,
}
impl<'a, 'b> Iterator for ArrayIter<'a, 'b> {
type Item = FieldType<'a>;
fn next(&mut self) -> Option<Self::Item> {
let this = self.idx;
self.idx += 1;
self.array.get(this)
}
}
impl<'a, T: Scalar + Into<FieldType<'a>>> SomeArray<'a> for &'a [BigEndian<T>]
where
BigEndian<T>: Copy, {
fn len(&self) -> usize {
(*self).len()
}
fn get(&self, idx: usize) -> Option<FieldType<'a>> {
(*self).get(idx).map(|val| val.get().into())
}
fn type_name(&self) -> &str {
let full_name = std::any::type_name::<T>();
full_name.split("::").last().unwrap_or(full_name)
}
}
impl<'a> SomeArray<'a> for &'a [u8] {
fn type_name(&self) -> &str {
"u8"
}
fn len(&self) -> usize {
(*self).len()
}
fn get(&self, idx: usize) -> Option<FieldType<'a>> {
(*self).get(idx).copied().map(Into::into)
}
}
impl<'a> SomeArray<'a> for Box<dyn SomeArray<'a> + 'a> {
fn type_name(&self) -> &str {
self.deref().type_name()
}
fn len(&self) -> usize {
self.deref().len()
}
fn get(&self, idx: usize) -> Option<FieldType<'a>> {
self.deref().get(idx)
}
}
pub trait SomeString<'a> {
fn iter_chars(&self) -> Box<dyn Iterator<Item = char> + 'a>;
}
impl<'a> SomeString<'a> for Box<dyn SomeString<'a> + 'a> {
fn iter_chars(&self) -> Box<dyn Iterator<Item = char> + 'a> {
self.deref().iter_chars()
}
}
struct ArrayOfRecords<'a, T> {
pub(crate) type_name: &'static str,
pub(crate) data: FontData<'a>,
pub(crate) records: &'a [T],
}
struct ComputedArrayOfRecords<'a, T: ReadArgs> {
pub(crate) type_name: &'static str,
pub(crate) data: FontData<'a>,
pub(crate) array: ComputedArray<'a, T>,
}
struct VarLenArrayOfRecords<'a, T> {
pub(crate) type_name: &'static str,
pub(crate) data: FontData<'a>,
pub(crate) array: VarLenArray<'a, T>,
}
impl<'a, T> SomeArray<'a> for ComputedArrayOfRecords<'a, T>
where
T: FontReadWithArgs<'a> + ComputeSize + SomeRecord<'a> + 'a,
T::Args: Copy + 'static,
Self: 'a,
{
fn len(&self) -> usize {
self.array.len()
}
fn get(&self, idx: usize) -> Option<FieldType<'a>> {
self.array
.get(idx)
.ok()
.map(|record| record.traverse(self.data).into())
}
fn type_name(&self) -> &str {
self.type_name
}
}
impl<'a, T: SomeRecord<'a> + Clone> SomeArray<'a> for ArrayOfRecords<'a, T> {
fn type_name(&self) -> &str {
self.type_name
}
fn len(&self) -> usize {
self.records.len()
}
fn get(&self, idx: usize) -> Option<FieldType<'a>> {
self.records
.get(idx)
.map(|record| record.clone().traverse(self.data).into())
}
}
impl<'a, T> SomeArray<'a> for VarLenArrayOfRecords<'a, T>
where
T: FontRead<'a> + VarSize + SomeRecord<'a> + 'a,
Self: 'a,
{
fn len(&self) -> usize {
self.array.iter().count()
}
fn get(&self, idx: usize) -> Option<FieldType<'a>> {
self.array
.get(idx)?
.ok()
.map(|record| record.traverse(self.data).into())
}
fn type_name(&self) -> &str {
self.type_name
}
}
impl<'a> Field<'a> {
pub fn new(name: &'static str, value: impl Into<FieldType<'a>>) -> Self {
Field {
name,
value: value.into(),
}
}
}
struct DebugPrintTable<'a, 'b>(pub &'b (dyn SomeTable<'a> + 'a));
struct DebugPrintArray<'a, 'b>(pub &'b (dyn SomeArray<'a> + 'a));
impl<'a> Debug for FieldType<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::I8(arg0) => arg0.fmt(f),
Self::U8(arg0) => arg0.fmt(f),
Self::I16(arg0) => arg0.fmt(f),
Self::U16(arg0) => arg0.fmt(f),
Self::I32(arg0) => arg0.fmt(f),
Self::U32(arg0) => arg0.fmt(f),
Self::U24(arg0) => arg0.fmt(f),
Self::Tag(arg0) => arg0.fmt(f),
Self::FWord(arg0) => arg0.to_i16().fmt(f),
Self::UfWord(arg0) => arg0.to_u16().fmt(f),
Self::MajorMinor(arg0) => write!(f, "{}.{}", arg0.major, arg0.minor),
Self::Version16Dot16(arg0) => arg0.fmt(f),
Self::F2Dot14(arg0) => arg0.fmt(f),
Self::Fixed(arg0) => arg0.fmt(f),
Self::LongDateTime(arg0) => arg0.as_secs().fmt(f),
Self::GlyphId(arg0) => {
write!(f, "g")?;
arg0.to_u16().fmt(f)
}
Self::NameId(arg0) => arg0.fmt(f),
Self::StringOffset(string) => match &string.target {
Ok(arg0) => arg0.as_ref().fmt(f),
Err(_) => string.target.fmt(f),
},
Self::ArrayOffset(array) => match &array.target {
Ok(arg0) => arg0.as_ref().fmt(f),
Err(_) => array.target.fmt(f),
},
Self::BareOffset(arg0) => write!(f, "0x{:04X}", arg0.to_u32()),
Self::ResolvedOffset(ResolvedOffset {
target: Ok(arg0), ..
}) => arg0.fmt(f),
Self::ResolvedOffset(arg0) => arg0.target.fmt(f),
Self::Record(arg0) => (arg0 as &(dyn SomeTable<'a> + 'a)).fmt(f),
Self::Array(arg0) => arg0.fmt(f),
Self::Unknown => write!(f, "no repr available"),
}
}
}
impl<'a, 'b> std::fmt::Debug for DebugPrintTable<'a, 'b> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let mut debug_struct = f.debug_struct(self.0.type_name());
for field in self.0.iter() {
debug_struct.field(field.name, &field.value);
}
debug_struct.finish()
}
}
impl<'a> Debug for dyn SomeTable<'a> + 'a {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
DebugPrintTable(self).fmt(f)
}
}
impl<'a> Debug for dyn SomeString<'a> + 'a {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "\"")?;
for c in self.iter_chars() {
write!(f, "{c}")?
}
write!(f, "\"")
}
}
impl<'a, 'b> std::fmt::Debug for DebugPrintArray<'a, 'b> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let mut debug_list = f.debug_list();
let mut idx = 0;
while let Some(item) = self.0.get(idx) {
idx += 1;
debug_list.entry(&item);
}
debug_list.finish()
}
}
impl<'a> Debug for dyn SomeArray<'a> + 'a {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
DebugPrintArray(self).fmt(f)
}
}
impl std::fmt::Display for OffsetType {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{:+}", self.to_u32())
}
}
impl<'a> SomeTable<'a> for RecordResolver<'a> {
fn type_name(&self) -> &str {
self.name
}
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
(self.get_field)(idx, self.data)
}
}
impl<'a> From<u8> for FieldType<'a> {
fn from(src: u8) -> FieldType<'a> {
FieldType::U8(src)
}
}
impl<'a> From<i8> for FieldType<'a> {
fn from(src: i8) -> FieldType<'a> {
FieldType::I8(src)
}
}
impl<'a> From<u16> for FieldType<'a> {
fn from(src: u16) -> FieldType<'a> {
FieldType::U16(src)
}
}
impl<'a> From<i16> for FieldType<'a> {
fn from(src: i16) -> FieldType<'a> {
FieldType::I16(src)
}
}
impl<'a> From<u32> for FieldType<'a> {
fn from(src: u32) -> FieldType<'a> {
FieldType::U32(src)
}
}
impl<'a> From<i32> for FieldType<'a> {
fn from(src: i32) -> FieldType<'a> {
FieldType::I32(src)
}
}
impl<'a> From<Uint24> for FieldType<'a> {
fn from(src: Uint24) -> FieldType<'a> {
FieldType::U24(src)
}
}
impl<'a> From<Tag> for FieldType<'a> {
fn from(src: Tag) -> FieldType<'a> {
FieldType::Tag(src)
}
}
impl<'a> From<FWord> for FieldType<'a> {
fn from(src: FWord) -> FieldType<'a> {
FieldType::FWord(src)
}
}
impl<'a> From<UfWord> for FieldType<'a> {
fn from(src: UfWord) -> FieldType<'a> {
FieldType::UfWord(src)
}
}
impl<'a> From<Fixed> for FieldType<'a> {
fn from(src: Fixed) -> FieldType<'a> {
FieldType::Fixed(src)
}
}
impl<'a> From<F2Dot14> for FieldType<'a> {
fn from(src: F2Dot14) -> FieldType<'a> {
FieldType::F2Dot14(src)
}
}
impl<'a> From<LongDateTime> for FieldType<'a> {
fn from(src: LongDateTime) -> FieldType<'a> {
FieldType::LongDateTime(src)
}
}
impl<'a> From<MajorMinor> for FieldType<'a> {
fn from(src: MajorMinor) -> FieldType<'a> {
FieldType::MajorMinor(src)
}
}
impl<'a> From<Version16Dot16> for FieldType<'a> {
fn from(src: Version16Dot16) -> FieldType<'a> {
FieldType::Version16Dot16(src)
}
}
impl<'a> From<GlyphId> for FieldType<'a> {
fn from(src: GlyphId) -> FieldType<'a> {
FieldType::GlyphId(src)
}
}
impl<'a> From<NameId> for FieldType<'a> {
fn from(src: NameId) -> FieldType<'a> {
FieldType::NameId(src)
}
}
impl<'a> From<RecordResolver<'a>> for FieldType<'a> {
fn from(src: RecordResolver<'a>) -> Self {
FieldType::Record(src)
}
}
impl<'a, T: SomeArray<'a> + 'a> From<T> for FieldType<'a> {
fn from(src: T) -> Self {
FieldType::Array(Box::new(src))
}
}
impl From<Offset16> for OffsetType {
fn from(src: Offset16) -> OffsetType {
OffsetType::Offset16(src.to_u32() as u16)
}
}
impl From<Offset24> for OffsetType {
fn from(src: Offset24) -> OffsetType {
OffsetType::Offset24(Uint24::new(src.to_u32()))
}
}
impl From<Offset32> for OffsetType {
fn from(src: Offset32) -> OffsetType {
OffsetType::Offset32(src.to_u32())
}
}
impl<'a> From<Offset16> for FieldType<'a> {
fn from(src: Offset16) -> FieldType<'a> {
FieldType::BareOffset(src.into())
}
}
impl<'a> From<Offset24> for FieldType<'a> {
fn from(src: Offset24) -> FieldType<'a> {
FieldType::BareOffset(src.into())
}
}
impl<'a> From<Offset32> for FieldType<'a> {
fn from(src: Offset32) -> FieldType<'a> {
FieldType::BareOffset(src.into())
}
}
impl<T: Into<OffsetType> + Clone> From<Nullable<T>> for OffsetType {
fn from(src: Nullable<T>) -> Self {
src.offset().clone().into()
}
}