Skip to main content

mf2_runtime/
sink.rs

1//! Where formatted text and errors go: minimal traits, not `core::fmt::Write`
2//! (B12). A client supplies its own sink (a DOM or SSR buffer adapter); the
3//! implementations for `String` and `Vec` here grow with `try_reserve` and
4//! drop what does not fit rather than abort.
5
6use alloc::string::String;
7use alloc::vec::Vec;
8
9use mf2_catalog::{Catalog, StrRef};
10
11use crate::error::FormatError;
12
13/// A text sink.
14pub trait Sink {
15    /// Appends `s`.
16    fn push_str(&mut self, s: &str);
17
18    /// Appends the catalog string `r` — the seam for catalog text as JS
19    /// strings (`plans/stretch_goals_after_v1/prob_builtin_strings.md` §7):
20    /// the evaluator writes catalog text only through this method. Returns
21    /// `false` (and writes nothing) when the string is not valid (F4).
22    #[doc(hidden)]
23    fn push_catalog_text(&mut self, catalog: &Catalog, r: StrRef) -> bool {
24        match catalog.text(r) {
25            Some(s) => {
26                self.push_str(s);
27                true
28            }
29            None => false,
30        }
31    }
32}
33
34impl Sink for String {
35    fn push_str(&mut self, s: &str) {
36        // `try_reserve`, then the append behind the test the append makes,
37        // so its infallible-growth branch is dead (see `scratch::try_push`).
38        let room = |t: &String| t.capacity().wrapping_sub(t.len()) >= s.len();
39        if (room(self) || self.try_reserve(s.len()).is_ok()) && room(self) {
40            String::push_str(self, s);
41        }
42    }
43}
44
45/// An error sink.
46pub trait ErrorSink {
47    /// Reports `e`.
48    fn error(&mut self, e: FormatError);
49}
50
51impl ErrorSink for Vec<FormatError> {
52    fn error(&mut self, e: FormatError) {
53        crate::scratch::try_push(self, e);
54    }
55}
56
57/// Discards errors: the release client's policy (`plans/03-runtime.md` §8).
58#[derive(Clone, Copy, Default, Debug)]
59pub struct NoErrors;
60
61impl ErrorSink for NoErrors {
62    #[inline]
63    fn error(&mut self, _: FormatError) {}
64}
65
66/// Receives the sub-parts of a formatted expression (a number's
67/// `minusSign`, `integer`, `decimal`, `fraction`, …).
68pub trait SubPartSink {
69    /// One sub-part: its kind and its text.
70    fn sub_part(&mut self, kind: &str, text: &str);
71}