use alloc::string::String;
use alloc::vec::Vec;
use core::cell::RefCell;
pub trait Platform {
fn diagnostics(&self) -> &dyn DiagnosticSink;
fn limits(&self) -> HostLimits {
HostLimits::default()
}
fn clock(&self) -> HostClock {
HostClock::default()
}
fn linebreak_start(&self, _request: LinebreakRequest<'_>) {}
fn linebreak_next(&self) -> Option<i32> {
None
}
}
impl<T> Platform for &T
where
T: Platform,
{
fn diagnostics(&self) -> &dyn DiagnosticSink {
(*self).diagnostics()
}
fn limits(&self) -> HostLimits {
(*self).limits()
}
fn clock(&self) -> HostClock {
(*self).clock()
}
fn linebreak_start(&self, request: LinebreakRequest<'_>) {
(*self).linebreak_start(request);
}
fn linebreak_next(&self) -> Option<i32> {
(*self).linebreak_next()
}
}
pub trait DiagnosticSink {
fn emit(&self, diagnostic: Diagnostic);
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Diagnostic {
pub severity: DiagnosticSeverity,
pub message: String,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum DiagnosticSeverity {
Info,
Warning,
Error,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct HostLimits {
pub max_input_bytes: usize,
pub max_resource_requests: usize,
pub max_layout_nodes: usize,
}
impl Default for HostLimits {
fn default() -> Self {
Self {
max_input_bytes: 1 << 20,
max_resource_requests: 256,
max_layout_nodes: 1 << 20,
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct HostClock {
pub seconds: i32,
pub micros: i32,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct LinebreakRequest<'a> {
pub font: i32,
pub locale: i32,
pub text: &'a [u16],
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum LimitError {
InputTooLarge {
actual: usize,
limit: usize,
},
TooManyResourceRequests {
actual: usize,
limit: usize,
},
TooManyLayoutNodes {
actual: usize,
limit: usize,
},
}
#[derive(Clone, Copy, Debug, Default)]
pub struct NoopDiagnosticSink;
impl DiagnosticSink for NoopDiagnosticSink {
fn emit(&self, _diagnostic: Diagnostic) {}
}
#[derive(Debug, Default)]
pub struct CollectingDiagnosticSink {
diagnostics: RefCell<Vec<Diagnostic>>,
}
impl CollectingDiagnosticSink {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn snapshot(&self) -> Vec<Diagnostic> {
self.diagnostics.borrow().clone()
}
#[must_use]
pub fn drain(&self) -> Vec<Diagnostic> {
self.diagnostics.borrow_mut().drain(..).collect()
}
#[must_use]
pub fn len(&self) -> usize {
self.diagnostics.borrow().len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.diagnostics.borrow().is_empty()
}
pub fn clear(&self) {
self.diagnostics.borrow_mut().clear();
}
}
impl DiagnosticSink for CollectingDiagnosticSink {
fn emit(&self, diagnostic: Diagnostic) {
self.diagnostics.borrow_mut().push(diagnostic);
}
}
#[derive(Clone, Debug)]
pub struct ConfigurablePlatform<D> {
diagnostics: D,
limits: HostLimits,
clock: HostClock,
}
impl<D> ConfigurablePlatform<D> {
#[must_use]
pub fn new(diagnostics: D, limits: HostLimits) -> Self {
Self {
diagnostics,
limits,
clock: HostClock::default(),
}
}
#[must_use]
pub fn with_diagnostics(diagnostics: D) -> Self {
Self {
diagnostics,
limits: HostLimits::default(),
clock: HostClock::default(),
}
}
#[must_use]
pub fn with_clock(mut self, clock: HostClock) -> Self {
self.clock = clock;
self
}
#[must_use]
pub fn diagnostic_sink(&self) -> &D {
&self.diagnostics
}
#[must_use]
pub fn host_limits(&self) -> HostLimits {
self.limits
}
#[must_use]
pub fn host_clock(&self) -> HostClock {
self.clock
}
}
impl<D> Platform for ConfigurablePlatform<D>
where
D: DiagnosticSink,
{
fn diagnostics(&self) -> &dyn DiagnosticSink {
&self.diagnostics
}
fn limits(&self) -> HostLimits {
self.limits
}
fn clock(&self) -> HostClock {
self.clock
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct NoopPlatform {
diagnostics: NoopDiagnosticSink,
limits: HostLimits,
clock: HostClock,
}
impl NoopPlatform {
#[must_use]
pub fn with_limits(limits: HostLimits) -> Self {
Self {
diagnostics: NoopDiagnosticSink,
limits,
clock: HostClock::default(),
}
}
#[must_use]
pub fn with_clock(clock: HostClock) -> Self {
Self {
diagnostics: NoopDiagnosticSink,
limits: HostLimits::default(),
clock,
}
}
}
impl Platform for NoopPlatform {
fn diagnostics(&self) -> &dyn DiagnosticSink {
&self.diagnostics
}
fn limits(&self) -> HostLimits {
self.limits
}
fn clock(&self) -> HostClock {
self.clock
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn collecting_diagnostic_sink_records_snapshots_and_drains() {
let sink = CollectingDiagnosticSink::new();
sink.emit(Diagnostic {
severity: DiagnosticSeverity::Warning,
message: "missing glyph".to_string(),
});
assert_eq!(sink.len(), 1);
assert_eq!(sink.snapshot()[0].message, "missing glyph");
assert_eq!(sink.drain()[0].severity, DiagnosticSeverity::Warning);
assert!(sink.is_empty());
}
}