1use alloc::string::String;
2use alloc::vec::Vec;
3use core::cell::RefCell;
4
5pub trait Platform {
7 fn diagnostics(&self) -> &dyn DiagnosticSink;
9
10 fn limits(&self) -> HostLimits {
12 HostLimits::default()
13 }
14
15 fn clock(&self) -> HostClock {
17 HostClock::default()
18 }
19
20 fn linebreak_start(&self, _request: LinebreakRequest<'_>) {}
22
23 fn linebreak_next(&self) -> Option<i32> {
25 None
26 }
27}
28
29impl<T> Platform for &T
30where
31 T: Platform,
32{
33 fn diagnostics(&self) -> &dyn DiagnosticSink {
34 (*self).diagnostics()
35 }
36
37 fn limits(&self) -> HostLimits {
38 (*self).limits()
39 }
40
41 fn clock(&self) -> HostClock {
42 (*self).clock()
43 }
44
45 fn linebreak_start(&self, request: LinebreakRequest<'_>) {
46 (*self).linebreak_start(request);
47 }
48
49 fn linebreak_next(&self) -> Option<i32> {
50 (*self).linebreak_next()
51 }
52}
53
54pub trait DiagnosticSink {
56 fn emit(&self, diagnostic: Diagnostic);
58}
59
60#[derive(Clone, Debug, PartialEq, Eq)]
62pub struct Diagnostic {
63 pub severity: DiagnosticSeverity,
65 pub message: String,
67}
68
69#[derive(Clone, Copy, Debug, PartialEq, Eq)]
71#[non_exhaustive]
72pub enum DiagnosticSeverity {
73 Info,
75 Warning,
77 Error,
79}
80
81#[derive(Clone, Copy, Debug, PartialEq, Eq)]
83pub struct HostLimits {
84 pub max_input_bytes: usize,
86 pub max_resource_requests: usize,
88 pub max_layout_nodes: usize,
90}
91
92impl Default for HostLimits {
93 fn default() -> Self {
94 Self {
95 max_input_bytes: 1 << 20,
96 max_resource_requests: 256,
97 max_layout_nodes: 1 << 20,
98 }
99 }
100}
101
102#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
104pub struct HostClock {
105 pub seconds: i32,
107 pub micros: i32,
109}
110
111#[derive(Clone, Copy, Debug, PartialEq, Eq)]
113pub struct LinebreakRequest<'a> {
114 pub font: i32,
116 pub locale: i32,
118 pub text: &'a [u16],
120}
121
122#[derive(Clone, Debug, PartialEq, Eq)]
124#[non_exhaustive]
125pub enum LimitError {
126 InputTooLarge {
128 actual: usize,
130 limit: usize,
132 },
133 TooManyResourceRequests {
135 actual: usize,
137 limit: usize,
139 },
140 TooManyLayoutNodes {
142 actual: usize,
144 limit: usize,
146 },
147}
148
149#[derive(Clone, Copy, Debug, Default)]
151pub struct NoopDiagnosticSink;
152
153impl DiagnosticSink for NoopDiagnosticSink {
154 fn emit(&self, _diagnostic: Diagnostic) {}
155}
156
157#[derive(Debug, Default)]
159pub struct CollectingDiagnosticSink {
160 diagnostics: RefCell<Vec<Diagnostic>>,
161}
162
163impl CollectingDiagnosticSink {
164 #[must_use]
166 pub fn new() -> Self {
167 Self::default()
168 }
169
170 #[must_use]
172 pub fn snapshot(&self) -> Vec<Diagnostic> {
173 self.diagnostics.borrow().clone()
174 }
175
176 #[must_use]
178 pub fn drain(&self) -> Vec<Diagnostic> {
179 self.diagnostics.borrow_mut().drain(..).collect()
180 }
181
182 #[must_use]
184 pub fn len(&self) -> usize {
185 self.diagnostics.borrow().len()
186 }
187
188 #[must_use]
190 pub fn is_empty(&self) -> bool {
191 self.diagnostics.borrow().is_empty()
192 }
193
194 pub fn clear(&self) {
196 self.diagnostics.borrow_mut().clear();
197 }
198}
199
200impl DiagnosticSink for CollectingDiagnosticSink {
201 fn emit(&self, diagnostic: Diagnostic) {
202 self.diagnostics.borrow_mut().push(diagnostic);
203 }
204}
205
206#[derive(Clone, Debug)]
208pub struct ConfigurablePlatform<D> {
209 diagnostics: D,
210 limits: HostLimits,
211 clock: HostClock,
212}
213
214impl<D> ConfigurablePlatform<D> {
215 #[must_use]
217 pub fn new(diagnostics: D, limits: HostLimits) -> Self {
218 Self {
219 diagnostics,
220 limits,
221 clock: HostClock::default(),
222 }
223 }
224
225 #[must_use]
227 pub fn with_diagnostics(diagnostics: D) -> Self {
228 Self {
229 diagnostics,
230 limits: HostLimits::default(),
231 clock: HostClock::default(),
232 }
233 }
234
235 #[must_use]
237 pub fn with_clock(mut self, clock: HostClock) -> Self {
238 self.clock = clock;
239 self
240 }
241
242 #[must_use]
244 pub fn diagnostic_sink(&self) -> &D {
245 &self.diagnostics
246 }
247
248 #[must_use]
250 pub fn host_limits(&self) -> HostLimits {
251 self.limits
252 }
253
254 #[must_use]
256 pub fn host_clock(&self) -> HostClock {
257 self.clock
258 }
259}
260
261impl<D> Platform for ConfigurablePlatform<D>
262where
263 D: DiagnosticSink,
264{
265 fn diagnostics(&self) -> &dyn DiagnosticSink {
266 &self.diagnostics
267 }
268
269 fn limits(&self) -> HostLimits {
270 self.limits
271 }
272
273 fn clock(&self) -> HostClock {
274 self.clock
275 }
276}
277
278#[derive(Clone, Copy, Debug, Default)]
280pub struct NoopPlatform {
281 diagnostics: NoopDiagnosticSink,
282 limits: HostLimits,
283 clock: HostClock,
284}
285
286impl NoopPlatform {
287 #[must_use]
289 pub fn with_limits(limits: HostLimits) -> Self {
290 Self {
291 diagnostics: NoopDiagnosticSink,
292 limits,
293 clock: HostClock::default(),
294 }
295 }
296
297 #[must_use]
299 pub fn with_clock(clock: HostClock) -> Self {
300 Self {
301 diagnostics: NoopDiagnosticSink,
302 limits: HostLimits::default(),
303 clock,
304 }
305 }
306}
307
308impl Platform for NoopPlatform {
309 fn diagnostics(&self) -> &dyn DiagnosticSink {
310 &self.diagnostics
311 }
312
313 fn limits(&self) -> HostLimits {
314 self.limits
315 }
316
317 fn clock(&self) -> HostClock {
318 self.clock
319 }
320}
321
322#[cfg(test)]
323mod tests {
324 use super::*;
325
326 #[test]
327 fn collecting_diagnostic_sink_records_snapshots_and_drains() {
328 let sink = CollectingDiagnosticSink::new();
329
330 sink.emit(Diagnostic {
331 severity: DiagnosticSeverity::Warning,
332 message: "missing glyph".to_string(),
333 });
334
335 assert_eq!(sink.len(), 1);
336 assert_eq!(sink.snapshot()[0].message, "missing glyph");
337 assert_eq!(sink.drain()[0].severity, DiagnosticSeverity::Warning);
338 assert!(sink.is_empty());
339 }
340
341}