blob: 6493a4e6d6d0eaa00ed5c7b3264df73c2796bff2 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#![allow(deprecated)]
use {
crate::{font_catalog::TypefaceInAssetIndex, serde_ext},
anyhow::Error,
serde::Deserialize,
std::path::Path,
};
/// Serializable representation of a ".fontcfg.json" file, which includes fallback chains.
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
#[serde(tag = "version")]
enum ProductConfigWrapper {
#[serde(rename = "1")]
Version1(ProductConfigInternal),
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Hash)]
struct ProductConfigInternal {
/// A sequence of typeface identifiers representing a fallback chain.
pub fallback_chain: Vec<TypefaceIdOrFileName>,
/// Runtime settings for the font provider service.
#[serde(default)]
pub settings: Settings,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Hash)]
#[serde(untagged)]
enum TypefaceIdOrFileName {
TypefaceId(TypefaceId),
FileName(String),
}
impl Into<TypefaceId> for TypefaceIdOrFileName {
fn into(self) -> TypefaceId {
match self {
TypefaceIdOrFileName::TypefaceId(typeface_id) => typeface_id,
TypefaceIdOrFileName::FileName(file_name) => TypefaceId { file_name, index: None },
}
}
}
/// Reference to a typeface, for use in specifying a fallback order.
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Hash)]
pub struct TypefaceId {
/// File name of the asset.
pub file_name: String,
/// Index of the typeface in the file. If absent, treat as all of the typefaces in the file.
pub index: Option<TypefaceInAssetIndex>,
}
#[cfg(test)]
impl TypefaceId {
/// Creates a new `TypefaceId` representing a single typeface in the given file.
///
/// - `file_name`: The file name of the font asset.
/// - `index`: The index of the typeface in the given file. See [`TypefaceInAssetIndex`].
pub(crate) fn new(
file_name: impl Into<String>,
index: impl Into<TypefaceInAssetIndex>,
) -> Self {
Self { file_name: file_name.into(), index: Some(index.into()) }
}
}
/// An in-memory representation of a ".fontcfg.json" file.
#[derive(Clone, Debug, Eq, PartialEq, Hash, Default)]
pub struct ProductConfig {
pub fallback_chain: Vec<TypefaceId>,
pub settings: Settings,
}
impl ProductConfig {
/// Loads the `ProductConfig` from a ".fontcfg.json" or ".fontcfg.json5" file.
pub fn load_from_path<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
let wrapper: ProductConfigWrapper = serde_ext::load_from_path(path)?;
match wrapper {
ProductConfigWrapper::Version1(ProductConfigInternal { fallback_chain, settings }) => {
let fallback_chain = fallback_chain
.into_iter()
.map(|id_or_filename| id_or_filename.into())
.collect();
Ok(ProductConfig { fallback_chain, settings })
}
}
}
/// Iterates over the `TypefaceId`s in the configuration's font fallback chain.
pub fn iter_fallback_chain(&self) -> impl Iterator<Item = &TypefaceId> {
self.fallback_chain.iter()
}
}
/// Runtime settings for the font provider service.
#[derive(Clone, Debug, Eq, PartialEq, Hash, Default, Deserialize)]
pub struct Settings {
/// Maximum size of the font service's in-memory asset cache, in bytes.
pub cache_size_bytes: Option<u64>,
}
#[cfg(test)]
mod tests {
use {
super::*, anyhow::Error, pretty_assertions::assert_eq, serde_json::json, std::io::Write,
tempfile::NamedTempFile,
};
#[test]
fn test_load_from_path() -> Result<(), Error> {
let contents = json!(
{
"version": "1",
"fallback_chain": [
"a.ttf",
"b.ttf",
{ "file_name": "c.ttf", "index": 1 },
{ "file_name": "d.ttf" },
{ "file_name": "e.ttf", "index": 0 }
],
"settings": {
"cache_size_bytes": 5000123
}
})
.to_string();
let mut file = NamedTempFile::new()?;
file.write_all(contents.as_bytes())?;
let actual = ProductConfig::load_from_path(file.path())?;
let expected = ProductConfig {
fallback_chain: vec![
TypefaceId { file_name: "a.ttf".to_string(), index: None },
TypefaceId { file_name: "b.ttf".to_string(), index: None },
TypefaceId { file_name: "c.ttf".to_string(), index: Some(TypefaceInAssetIndex(1)) },
TypefaceId { file_name: "d.ttf".to_string(), index: None },
TypefaceId { file_name: "e.ttf".to_string(), index: Some(TypefaceInAssetIndex(0)) },
],
settings: Settings { cache_size_bytes: Some(5_000_123) },
};
assert_eq!(actual, expected);
Ok(())
}
}