webfont_generator/result/
mod.rs1#[cfg(feature = "fs")]
2mod regeneration;
3#[cfg(all(test, feature = "fs"))]
4mod tests;
5
6#[cfg(feature = "fs")]
7use std::collections::HashMap;
8use std::sync::Arc;
9#[cfg(feature = "fs")]
10use std::sync::{Mutex, OnceLock};
11
12#[cfg(feature = "fs")]
13use serde_json::{Map, Value};
14
15#[cfg(feature = "fs")]
16use crate::incremental::RegenerationState;
17#[cfg(feature = "fs")]
18use crate::input::{LoadedSvgSource, ResolvedFileGenerationOptions};
19#[cfg(feature = "fs")]
20use crate::rendering::{CachedTemplateData, CarriedRenderCache};
21#[cfg(feature = "bench")]
22use crate::types::GlyphChange;
23
24#[derive(Clone, Default)]
28pub(crate) struct FontOutputs {
29 pub(crate) svg_font: Option<Arc<String>>,
30 pub(crate) ttf_font: Option<Arc<Vec<u8>>>,
31 pub(crate) woff_font: Option<Arc<Vec<u8>>>,
32 pub(crate) woff2_font: Option<Arc<Vec<u8>>>,
33 pub(crate) eot_font: Option<Arc<Vec<u8>>>,
34}
35
36#[cfg(feature = "fs")]
39pub struct RegenerateError {
40 result: Option<Box<GenerateWebfontsResult>>,
41 source: std::io::Error,
42}
43
44#[cfg(feature = "fs")]
45impl RegenerateError {
46 pub(crate) fn new(result: Option<GenerateWebfontsResult>, source: std::io::Error) -> Self {
47 Self {
48 result: result.map(Box::new),
49 source,
50 }
51 }
52
53 pub fn into_result(self) -> Option<GenerateWebfontsResult> {
56 self.result.map(|result| *result)
57 }
58
59 pub fn into_parts(self) -> (Option<GenerateWebfontsResult>, std::io::Error) {
61 (self.result.map(|result| *result), self.source)
62 }
63}
64
65#[cfg(feature = "fs")]
66impl std::fmt::Debug for RegenerateError {
67 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68 formatter
69 .debug_struct("RegenerateError")
70 .field("recoverable", &self.result.is_some())
71 .field("source", &self.source)
72 .finish()
73 }
74}
75
76#[cfg(feature = "fs")]
77impl std::fmt::Display for RegenerateError {
78 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79 self.source.fmt(formatter)
80 }
81}
82
83#[cfg(feature = "fs")]
84impl std::error::Error for RegenerateError {
85 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
86 Some(&self.source)
87 }
88}
89
90#[cfg(feature = "fs")]
94pub struct GenerateWebfontsResult {
95 pub(crate) ts: OnceLock<Arc<String>>,
96 pub(crate) cached: OnceLock<Result<CachedTemplateData, String>>,
97 pub(crate) carried_render: Option<CarriedRenderCache>,
101 pub(crate) css_context: Option<Map<String, Value>>,
102 pub(crate) fonts: FontOutputs,
103 pub(crate) html_context: Option<Map<String, Value>>,
104 pub(crate) options: Arc<ResolvedFileGenerationOptions>,
105 pub(crate) regeneration_state: Arc<Mutex<Option<RegenerationState>>>,
106 pub(crate) source_files: Arc<Vec<LoadedSvgSource>>,
107}
108
109#[cfg(feature = "fs")]
110impl GenerateWebfontsResult {
111 pub(crate) fn snapshot_for_regeneration(&self, state: RegenerationState) -> Self {
112 Self {
113 cached: OnceLock::new(),
114 ts: self.ts.clone(),
115 carried_render: self.render_cache_source(),
116 css_context: self.css_context.clone(),
117 fonts: self.fonts.clone(),
118 html_context: self.html_context.clone(),
119 options: self.options.clone(),
120 regeneration_state: Arc::new(Mutex::new(Some(state))),
121 source_files: self.source_files.clone(),
122 }
123 }
124
125 pub(crate) fn require_ordinary_regeneration(&self) -> std::io::Result<()> {
126 if self.options.variants.is_some() {
127 return Err(std::io::Error::new(
128 std::io::ErrorKind::Unsupported,
129 "Single file lists do not support variant results; supply complete variant file sets.",
130 ));
131 }
132 Ok(())
133 }
134
135 pub(crate) fn take_regeneration_state(&self) -> std::io::Result<RegenerationState> {
136 if !self.options.incremental {
137 return Err(std::io::Error::new(
138 std::io::ErrorKind::InvalidInput,
139 "regenerate requires the font to be generated with `incremental` enabled.",
140 ));
141 }
142 self.regeneration_state
143 .lock()
144 .unwrap()
145 .take()
146 .ok_or_else(|| {
147 std::io::Error::new(
148 std::io::ErrorKind::WouldBlock,
149 "This result is already regenerating or has been replaced.",
150 )
151 })
152 }
153
154 #[cfg(feature = "bench")]
155 pub(crate) fn restore_regeneration_state(&self, state: RegenerationState) {
156 *self.regeneration_state.lock().unwrap() = Some(state);
157 }
158
159 pub(crate) fn seed_written_outputs(&self, written_outputs: HashMap<String, [u8; 16]>) {
160 if let Some(state) = self.regeneration_state.lock().unwrap().as_mut() {
161 state.written_outputs = written_outputs;
162 }
163 }
164
165 #[doc(hidden)]
166 #[cfg(feature = "bench")]
167 pub fn regenerate_owned_for_bench(
168 &self,
169 files: &crate::RegenerationFiles,
170 changes: &[(String, GlyphChange)],
171 ) -> std::io::Result<Self> {
172 let state = self.take_regeneration_state()?;
173 let mut replacement = self.snapshot_for_regeneration(state);
174 match replacement.regenerate(files, changes) {
175 Ok(()) => Ok(replacement),
176 Err(error) => {
177 if let Ok(state) = replacement.take_regeneration_state() {
178 self.restore_regeneration_state(state);
179 }
180 Err(error)
181 }
182 }
183 }
184
185 pub fn eot_bytes(&self) -> Option<&[u8]> {
187 self.fonts.eot_font.as_ref().map(|v| v.as_ref().as_slice())
188 }
189
190 pub fn svg_string(&self) -> Option<&str> {
192 self.fonts.svg_font.as_ref().map(|v| v.as_ref().as_str())
193 }
194
195 pub fn ttf_bytes(&self) -> Option<&[u8]> {
197 self.fonts.ttf_font.as_ref().map(|v| v.as_ref().as_slice())
198 }
199
200 pub fn woff_bytes(&self) -> Option<&[u8]> {
202 self.fonts.woff_font.as_ref().map(|v| v.as_ref().as_slice())
203 }
204
205 pub fn woff2_bytes(&self) -> Option<&[u8]> {
207 self.fonts
208 .woff2_font
209 .as_ref()
210 .map(|v| v.as_ref().as_slice())
211 }
212}