1use std::sync::Arc;
2
3use schemars::schema::{InstanceType, SchemaObject};
4
5/// The OpenType features that can be configured for a given font.
6#[derive(Default, Clone, Eq, PartialEq, Hash)]
7pub struct FontFeatures(pub Arc<Vec<(String, u32)>>);
8
9impl FontFeatures {
10 /// Get the tag name list of the font OpenType features
11 /// only enabled or disabled features are returned
12 pub fn tag_value_list(&self) -> &[(String, u32)] {
13 &self.0.as_slice()
14 }
15}
16
17impl std::fmt::Debug for FontFeatures {
18 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19 let mut debug = f.debug_struct("FontFeatures");
20 for (tag, value) in self.tag_value_list() {
21 debug.field(tag, value);
22 }
23
24 debug.finish()
25 }
26}
27
28#[derive(Debug, serde::Serialize, serde::Deserialize)]
29#[serde(untagged)]
30enum FeatureValue {
31 Bool(bool),
32 Number(serde_json::Number),
33}
34
35impl<'de> serde::Deserialize<'de> for FontFeatures {
36 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
37 where
38 D: serde::Deserializer<'de>,
39 {
40 use serde::de::{MapAccess, Visitor};
41 use std::fmt;
42
43 struct FontFeaturesVisitor;
44
45 impl<'de> Visitor<'de> for FontFeaturesVisitor {
46 type Value = FontFeatures;
47
48 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
49 formatter.write_str("a map of font features")
50 }
51
52 fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
53 where
54 M: MapAccess<'de>,
55 {
56 let mut feature_list = Vec::new();
57
58 while let Some((key, value)) =
59 access.next_entry::<String, Option<FeatureValue>>()?
60 {
61 if key.len() != 4 && !key.is_ascii() {
62 log::error!("Incorrect font feature tag: {}", key);
63 continue;
64 }
65 if let Some(value) = value {
66 match value {
67 FeatureValue::Bool(enable) => {
68 if enable {
69 feature_list.push((key, 1));
70 } else {
71 feature_list.push((key, 0));
72 }
73 }
74 FeatureValue::Number(value) => {
75 if value.is_u64() {
76 feature_list.push((key, value.as_u64().unwrap() as u32));
77 } else {
78 log::error!(
79 "Incorrect font feature value {} for feature tag {}",
80 value,
81 key
82 );
83 continue;
84 }
85 }
86 }
87 }
88 }
89
90 Ok(FontFeatures(Arc::new(feature_list)))
91 }
92 }
93
94 let features = deserializer.deserialize_map(FontFeaturesVisitor)?;
95 Ok(features)
96 }
97}
98
99impl serde::Serialize for FontFeatures {
100 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
101 where
102 S: serde::Serializer,
103 {
104 use serde::ser::SerializeMap;
105
106 let mut map = serializer.serialize_map(None)?;
107
108 for (tag, value) in self.tag_value_list() {
109 map.serialize_entry(tag, value)?;
110 }
111
112 map.end()
113 }
114}
115
116impl schemars::JsonSchema for FontFeatures {
117 fn schema_name() -> String {
118 "FontFeatures".into()
119 }
120
121 fn json_schema(_: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
122 let mut schema = SchemaObject::default();
123 schema.instance_type = Some(schemars::schema::SingleOrVec::Single(Box::new(
124 InstanceType::Object,
125 )));
126 {
127 let mut property = SchemaObject::default();
128 property.instance_type = Some(schemars::schema::SingleOrVec::Vec(vec![
129 InstanceType::Boolean,
130 InstanceType::Integer,
131 ]));
132 {
133 let mut number_constraints = property.number();
134 number_constraints.multiple_of = Some(1.0);
135 number_constraints.minimum = Some(0.0);
136 }
137 schema
138 .object()
139 .pattern_properties
140 .insert("[0-9a-zA-Z]{4}$".into(), property.into());
141 }
142 schema.into()
143 }
144}