#[macro_use]
pub mod serde_ext;
mod v1_to_v2;
pub mod v2;
use crate::serde_ext::*;
use crate::v2::FontsManifest as FontsManifestV2;
use anyhow::Error;
use char_set::CharSet;
use clonable_error::ClonableError;
use fidl_fuchsia_fonts::{GenericFontFamily, Slant, Width, WEIGHT_NORMAL};
use fuchsia_url::AbsolutePackageUrl;
use offset_string::OffsetString;
use serde::de::{self, Deserializer, Error as DeError};
use serde::ser::Serializer;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::fs::{self, File};
use std::io::BufReader;
use std::path::{Path, PathBuf};
use thiserror::Error;
#[derive(Debug, Deserialize, Serialize)]
#[serde(tag = "version")]
#[serde(remote = "Self")]
pub enum FontManifestWrapper {
#[serde(rename = "1")]
Version1(FontsManifest),
#[serde(rename = "2")]
Version2(FontsManifestV2),
}
impl<'de> Deserialize<'de> for FontManifestWrapper {
fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
where
D: Deserializer<'de>,
{
let mut map = serde_json::value::Map::deserialize(deserializer)?;
if map.get("version").is_none() {
map.insert("version".to_string(), serde_json::Value::String("1".to_string()));
}
Ok(FontManifestWrapper::deserialize(serde_json::Value::Object(map))
.map_err(de::Error::custom)?)
}
}
impl Serialize for FontManifestWrapper {
fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
where
S: Serializer,
{
FontManifestWrapper::serialize(self, serializer)
}
}
#[derive(Debug, Deserialize, Serialize)]
pub struct FontsManifest {
pub families: Vec<Family>,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct Family {
pub family: String,
pub aliases: Option<Vec<String>>,
pub fonts: Vec<Font>,
#[serde(default = "default_fallback")]
pub fallback: bool,
#[serde(
alias = "fallback_group",
default = "default_generic_family",
with = "OptGenericFontFamily"
)]
pub generic_family: Option<GenericFontFamily>,
}
pub type LanguageSet = Vec<String>;
#[derive(Debug, Deserialize, Serialize)]
pub struct Font {
pub asset: PathBuf,
#[serde(default = "default_index")]
pub index: u32,
#[serde(default = "default_slant", with = "SlantDef")]
pub slant: Slant,
#[serde(default = "default_weight")]
pub weight: u16,
#[serde(default = "default_width", with = "WidthDef")]
pub width: Width,
#[serde(
alias = "language",
default = "default_languages",
deserialize_with = "deserialize_languages"
)]
pub languages: LanguageSet,
#[serde(default = "default_package")]
pub package: Option<AbsolutePackageUrl>,
#[serde(
default,
deserialize_with = "deserialize_code_points",
serialize_with = "serialize_code_points"
)]
pub code_points: CharSet,
}
fn default_fallback() -> bool {
false
}
fn default_generic_family() -> Option<GenericFontFamily> {
None
}
fn default_index() -> u32 {
0
}
fn default_slant() -> Slant {
Slant::Upright
}
fn default_weight() -> u16 {
WEIGHT_NORMAL
}
fn default_width() -> Width {
Width::Normal
}
fn default_languages() -> LanguageSet {
LanguageSet::new()
}
fn default_package() -> Option<AbsolutePackageUrl> {
None
}
fn deserialize_languages<'d, D>(deserializer: D) -> Result<LanguageSet, D::Error>
where
D: Deserializer<'d>,
{
struct LanguageSetVisitor;
impl<'de> de::Visitor<'de> for LanguageSetVisitor {
type Value = Vec<String>;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("string or list of strings")
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(vec![s.to_string()])
}
fn visit_seq<S>(self, seq: S) -> Result<Self::Value, S::Error>
where
S: de::SeqAccess<'de>,
{
Deserialize::deserialize(de::value::SeqAccessDeserializer::new(seq))
}
}
deserializer.deserialize_any(LanguageSetVisitor)
}
fn deserialize_code_points<'d, D>(deserializer: D) -> Result<CharSet, D::Error>
where
D: Deserializer<'d>,
{
let offset_string = OffsetString::deserialize(deserializer)?;
CharSet::try_from(offset_string).map_err(|e| D::Error::custom(format!("{:?}", e)))
}
fn serialize_code_points<S>(code_points: &CharSet, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let offset_string: OffsetString = code_points.into();
offset_string.serialize(serializer)
}
impl FontsManifest {
pub fn load_from_file(path: &Path) -> Result<FontManifestWrapper, anyhow::Error> {
let path = fs::canonicalize(path)?;
let base_dir =
path.parent().ok_or_else(|| ManifestLoadError::InvalidPath { path: path.clone() })?;
let file = File::open(&path)
.map_err(|e| ManifestLoadError::ReadError { path: path.clone(), cause: e })?;
let mut wrapper: FontManifestWrapper =
serde_json::from_reader(BufReader::new(file)).map_err(|e| {
ManifestLoadError::ParseError { path: path.clone(), cause: Error::from(e).into() }
})?;
if let FontManifestWrapper::Version1(v1) = &mut wrapper {
for family in v1.families.iter_mut() {
for font in family.fonts.iter_mut() {
if font.asset.is_relative() {
font.asset = base_dir.join(font.asset.clone());
}
}
}
}
Ok(wrapper)
}
}
#[derive(Debug, Error)]
pub enum ManifestLoadError {
#[error("Invalid manifest path: {:?}", path)]
InvalidPath {
path: PathBuf,
},
#[error("Failed to read {:?}: {:?}", path, cause)]
ReadError {
path: PathBuf,
#[source]
cause: std::io::Error,
},
#[error("Failed to parse {:?}: {:?}", path, cause)]
ParseError {
path: PathBuf,
#[source]
cause: ClonableError,
},
}
#[cfg(test)]
mod tests {
use super::*;
use assert_matches::assert_matches;
#[test]
fn test_deserialize_manifest_version_v1_implicit() -> Result<(), Error> {
let json = r#"
{
"families": []
}
"#;
let wrapper: FontManifestWrapper = serde_json::from_str(json)?;
assert_matches!(wrapper, FontManifestWrapper::Version1(_));
Ok(())
}
#[test]
fn test_deserialize_manifest_version_v2() -> Result<(), Error> {
let json = r#"
{
"version": "2",
"families": [],
"fallback_chain": []
}
"#;
let wrapper: FontManifestWrapper = serde_json::from_str(json)?;
assert_matches!(wrapper, FontManifestWrapper::Version2(_));
Ok(())
}
}