1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
//! Traits for converting from parsed font data to their compile equivalents

use std::collections::BTreeSet;

use read::{FontData, ReadError};
use types::{BigEndian, Scalar};

use crate::{NullableOffsetMarker, OffsetMarker};

/// A trait for types that can fully resolve themselves.
///
/// This means that any offsets held in this type are resolved relative to the
/// start of the table itself (and not some parent table)
pub trait FromTableRef<T>: FromObjRef<T> {
    fn from_table_ref(from: &T) -> Self {
        let data = FontData::new(&[]);
        Self::from_obj_ref(from, data)
    }
}

/// A trait for types that can resolve themselves when provided data to resolve offsets.
///
/// It is possible that the generated object is malformed; for instance offsets
/// may be null where it is not allowed. This can be checked by calling [`validate`][]
/// on the generated object.
///
/// This is implemented for the majority of parse types. Those that are the base
/// for offset data ignore the provided data and use their own.
///
/// [`validate`]: [crate::Validate::validate]
pub trait FromObjRef<T: ?Sized>: Sized {
    /// Convert `from` to an instance of `Self`, using the provided data to resolve offsets.
    fn from_obj_ref(from: &T, data: FontData) -> Self;
}

/// A conversion from a parsed font object type to an owned version, resolving
/// offsets.
///
/// You should avoid implementing this trait manually. Like [`std::convert::Into`],
/// it is provided as a blanket impl when you implement [`FromObjRef<T>`].
pub trait ToOwnedObj<T> {
    /// Convert this type into `T`, using the provided data to resolve any offsets.
    fn to_owned_obj(&self, data: FontData) -> T;
}

/// A conversion from a fully resolveable parsed font table to its owned equivalent.
///
/// As with [`ToOwnedObj`], you should not need to implement this manually.
pub trait ToOwnedTable<T>: ToOwnedObj<T> {
    fn to_owned_table(&self) -> T;
}

impl<U, T> ToOwnedObj<U> for T
where
    U: FromObjRef<T>,
{
    fn to_owned_obj(&self, data: FontData) -> U {
        U::from_obj_ref(self, data)
    }
}

impl<U, T> ToOwnedTable<U> for T
where
    U: FromTableRef<T>,
{
    fn to_owned_table(&self) -> U {
        U::from_table_ref(self)
    }
}

impl<T> FromObjRef<BigEndian<T>> for T
where
    T: Scalar,
    BigEndian<T>: Copy,
{
    fn from_obj_ref(from: &BigEndian<T>, _: FontData) -> Self {
        from.get()
    }
}

// we need this because we special case &[u8], eliding the BigEndian wrapper.
impl FromObjRef<u8> for u8 {
    fn from_obj_ref(from: &u8, _data: FontData) -> Self {
        *from
    }
}

impl<T, U> FromObjRef<&[U]> for Vec<T>
where
    T: FromObjRef<U>,
{
    fn from_obj_ref(from: &&[U], data: FontData) -> Self {
        from.iter().map(|item| item.to_owned_obj(data)).collect()
    }
}

impl<T, U> FromObjRef<&[U]> for BTreeSet<T>
where
    T: FromObjRef<U> + std::cmp::Ord,
{
    fn from_obj_ref(from: &&[U], data: FontData) -> Self {
        from.iter().map(|item| item.to_owned_obj(data)).collect()
    }
}

// A blanket impl to cover converting any Option<T> if T is convertable
impl<T: FromObjRef<U>, U> FromObjRef<Option<U>> for Option<T> {
    fn from_obj_ref(from: &Option<U>, data: FontData) -> Self {
        from.as_ref().map(|inner| T::from_obj_ref(inner, data))
    }
}

// A blanket impl to cover converting any Option<T> if T is convertable
impl<T: FromTableRef<U>, U> FromTableRef<Option<U>> for Option<T> {
    fn from_table_ref(from: &Option<U>) -> Self {
        from.as_ref().map(ToOwnedTable::to_owned_table)
    }
}

/* blanket impls converting resolved offsets to offsetmarkers */

impl<T: FromObjRef<U> + Default, U, const N: usize> FromObjRef<Result<U, ReadError>>
    for OffsetMarker<T, N>
{
    fn from_obj_ref(from: &Result<U, ReadError>, data: FontData) -> Self {
        match from {
            Err(_) => OffsetMarker::default(),
            Ok(table) => OffsetMarker::new(table.to_owned_obj(data)),
        }
    }
}

impl<T: FromObjRef<U>, U, const N: usize> FromObjRef<Option<Result<U, ReadError>>>
    for NullableOffsetMarker<T, N>
{
    fn from_obj_ref(from: &Option<Result<U, ReadError>>, data: FontData) -> Self {
        match from {
            Some(Ok(table)) => NullableOffsetMarker::new(Some(table.to_owned_obj(data))),
            _ => NullableOffsetMarker::new(None),
        }
    }
}

// used for bare offsets
impl<T: FromTableRef<U> + Default, U, const N: usize> FromTableRef<Result<U, ReadError>>
    for OffsetMarker<T, N>
{
    fn from_table_ref(from: &Result<U, ReadError>) -> Self {
        match from {
            Err(_) => OffsetMarker::default(),
            Ok(table) => OffsetMarker::new(table.to_owned_table()),
        }
    }
}

// convert bare nullable/versioned offsets to NullableOffsetMarker
impl<T: FromTableRef<U>, U, const N: usize> FromTableRef<Option<Result<U, ReadError>>>
    for NullableOffsetMarker<T, N>
{
    fn from_table_ref(from: &Option<Result<U, ReadError>>) -> Self {
        match from {
            Some(Ok(table)) => NullableOffsetMarker::new(Some(table.to_owned_table())),
            _ => NullableOffsetMarker::new(None),
        }
    }
}