#[cfg(feature = "fs")]
mod regeneration;
#[cfg(all(test, feature = "fs"))]
mod tests;
#[cfg(feature = "fs")]
use std::collections::HashMap;
use std::sync::Arc;
#[cfg(feature = "fs")]
use std::sync::{Mutex, OnceLock};
#[cfg(feature = "fs")]
use serde_json::{Map, Value};
#[cfg(feature = "fs")]
use crate::incremental::RegenerationState;
#[cfg(feature = "fs")]
use crate::input::{LoadedSvgSource, ResolvedFileGenerationOptions};
#[cfg(feature = "fs")]
use crate::rendering::{CachedTemplateData, CarriedRenderCache};
#[cfg(feature = "bench")]
use crate::types::GlyphChange;
#[derive(Clone, Default)]
pub(crate) struct FontOutputs {
pub(crate) svg_font: Option<Arc<String>>,
pub(crate) ttf_font: Option<Arc<Vec<u8>>>,
pub(crate) woff_font: Option<Arc<Vec<u8>>>,
pub(crate) woff2_font: Option<Arc<Vec<u8>>>,
pub(crate) eot_font: Option<Arc<Vec<u8>>>,
}
#[cfg(feature = "fs")]
pub struct RegenerateError {
result: Option<Box<GenerateWebfontsResult>>,
source: std::io::Error,
}
#[cfg(feature = "fs")]
impl RegenerateError {
pub(crate) fn new(result: Option<GenerateWebfontsResult>, source: std::io::Error) -> Self {
Self {
result: result.map(Box::new),
source,
}
}
pub fn into_result(self) -> Option<GenerateWebfontsResult> {
self.result.map(|result| *result)
}
pub fn into_parts(self) -> (Option<GenerateWebfontsResult>, std::io::Error) {
(self.result.map(|result| *result), self.source)
}
}
#[cfg(feature = "fs")]
impl std::fmt::Debug for RegenerateError {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("RegenerateError")
.field("recoverable", &self.result.is_some())
.field("source", &self.source)
.finish()
}
}
#[cfg(feature = "fs")]
impl std::fmt::Display for RegenerateError {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.source.fmt(formatter)
}
}
#[cfg(feature = "fs")]
impl std::error::Error for RegenerateError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.source)
}
}
#[cfg(feature = "fs")]
pub struct GenerateWebfontsResult {
pub(crate) ts: OnceLock<Arc<String>>,
pub(crate) cached: OnceLock<Result<CachedTemplateData, String>>,
pub(crate) carried_render: Option<CarriedRenderCache>,
pub(crate) css_context: Option<Map<String, Value>>,
pub(crate) fonts: FontOutputs,
pub(crate) html_context: Option<Map<String, Value>>,
pub(crate) options: Arc<ResolvedFileGenerationOptions>,
pub(crate) regeneration_state: Arc<Mutex<Option<RegenerationState>>>,
pub(crate) source_files: Arc<Vec<LoadedSvgSource>>,
}
#[cfg(feature = "fs")]
impl GenerateWebfontsResult {
pub(crate) fn snapshot_for_regeneration(&self, state: RegenerationState) -> Self {
Self {
cached: OnceLock::new(),
ts: self.ts.clone(),
carried_render: self.render_cache_source(),
css_context: self.css_context.clone(),
fonts: self.fonts.clone(),
html_context: self.html_context.clone(),
options: self.options.clone(),
regeneration_state: Arc::new(Mutex::new(Some(state))),
source_files: self.source_files.clone(),
}
}
pub(crate) fn require_ordinary_regeneration(&self) -> std::io::Result<()> {
if self.options.variants.is_some() {
return Err(std::io::Error::new(
std::io::ErrorKind::Unsupported,
"Single file lists do not support variant results; supply complete variant file sets.",
));
}
Ok(())
}
pub(crate) fn take_regeneration_state(&self) -> std::io::Result<RegenerationState> {
if !self.options.incremental {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"regenerate requires the font to be generated with `incremental` enabled.",
));
}
self.regeneration_state
.lock()
.unwrap()
.take()
.ok_or_else(|| {
std::io::Error::new(
std::io::ErrorKind::WouldBlock,
"This result is already regenerating or has been replaced.",
)
})
}
#[cfg(feature = "bench")]
pub(crate) fn restore_regeneration_state(&self, state: RegenerationState) {
*self.regeneration_state.lock().unwrap() = Some(state);
}
pub(crate) fn seed_written_outputs(&self, written_outputs: HashMap<String, [u8; 16]>) {
if let Some(state) = self.regeneration_state.lock().unwrap().as_mut() {
state.written_outputs = written_outputs;
}
}
#[doc(hidden)]
#[cfg(feature = "bench")]
pub fn regenerate_owned_for_bench(
&self,
files: &crate::RegenerationFiles,
changes: &[(String, GlyphChange)],
) -> std::io::Result<Self> {
let state = self.take_regeneration_state()?;
let mut replacement = self.snapshot_for_regeneration(state);
match replacement.regenerate(files, changes) {
Ok(()) => Ok(replacement),
Err(error) => {
if let Ok(state) = replacement.take_regeneration_state() {
self.restore_regeneration_state(state);
}
Err(error)
}
}
}
pub fn eot_bytes(&self) -> Option<&[u8]> {
self.fonts.eot_font.as_ref().map(|v| v.as_ref().as_slice())
}
pub fn svg_string(&self) -> Option<&str> {
self.fonts.svg_font.as_ref().map(|v| v.as_ref().as_str())
}
pub fn ttf_bytes(&self) -> Option<&[u8]> {
self.fonts.ttf_font.as_ref().map(|v| v.as_ref().as_slice())
}
pub fn woff_bytes(&self) -> Option<&[u8]> {
self.fonts.woff_font.as_ref().map(|v| v.as_ref().as_slice())
}
pub fn woff2_bytes(&self) -> Option<&[u8]> {
self.fonts
.woff2_font
.as_ref()
.map(|v| v.as_ref().as_slice())
}
}