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