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}