Skip to main content

scratch_test_value/
quirk_sink.rs

1/// Interface for messages regarding program quriks the user should be warned about.
2///
3/// Often Scratch tries to do _something_ so that it doesn't have to
4/// raise an exception or fatal error. It allows accessing a list
5/// out of bounds, computes modulo with floating point arguments, ...
6///
7/// As the interpreter has to mimic this behaviour it also generates
8/// warnings when such behaviour is encountered as it could be considered
9/// bad practice. Every "quirky" operation can define an own type with
10/// possible warnings by requiring a _qurik sink_ of the type.
11///
12/// It then would perform it's operation according to Scratch semantics
13/// while still being able to emit warnings to the sink.
14///
15/// `()` is a valid sink for **any** message type so it can be used
16/// to ignore any message an operation might send.
17pub trait QuirkSink<Msg> {
18    fn put(&mut self, msg: Msg);
19}
20
21impl<M> QuirkSink<M> for () {
22    fn put(&mut self, _: M) {}
23}
24
25#[allow(unused)]
26pub(crate) struct ScopedQuirkSink<'a, QS, F> {
27    inner_sink: &'a mut QS,
28    wrapper: F,
29}
30
31impl<'a, QS, F, I, O> QuirkSink<I> for ScopedQuirkSink<'a, QS, F>
32where
33    F: Fn(I) -> O,
34    QS: QuirkSink<O>,
35{
36    fn put(&mut self, msg: I) {
37        self.inner_sink.put((self.wrapper)(msg));
38    }
39}
40
41#[allow(unused)]
42pub(crate) trait ScopableQuirkSink<I, O> {
43    fn scope_map<F>(&mut self, func: F) -> ScopedQuirkSink<'_, Self, F>
44    where
45        Self: Sized,
46        F: Fn(I) -> O,
47        Self: QuirkSink<O>;
48}
49
50impl<QS, I, O> ScopableQuirkSink<I, O> for QS
51where
52    QS: QuirkSink<O>,
53{
54    fn scope_map<F>(&mut self, func: F) -> ScopedQuirkSink<'_, Self, F>
55    where
56        Self: Sized,
57        F: Fn(I) -> O,
58        Self: QuirkSink<O>,
59    {
60        ScopedQuirkSink {
61            inner_sink: self,
62            wrapper: func,
63        }
64    }
65}