Skip to main content

scratch_test_value/conversions/
to_bool.rs

1use crate::{QuirkSink, SValue};
2
3/// Generated by [`SValue::q_as_bool`]
4///
5/// As Scratch blocks don't allow placing an expression block inside
6/// of an comparison context
7/// (block shapes, round blocks can't be placed in wholes of shape <..>)
8/// this shouldn't occur for normal user programs.
9pub enum SValueToBoolQ {
10    /// Boolean conversion for empty string returned [false]
11    EmptyStringTreatedAsFalse,
12    /// Boolean conversion of a non-empty string returned [true]
13    NonEmptyStringTreatedAsTrue,
14    /// The integer or float number zero was converted to [false]
15    ZeroToFalse,
16    /// The integer or float number one was converted to [true]
17    OneToTrue,
18    /// An integer that was neither zero nor one was converted to [true]
19    IntToBoolNotLossless(i64),
20    /// A float that was neither zero nor one was converted to [true]
21    FloatToBoolNotLossless(f64),
22}
23
24impl SValue {
25    /// Converts the value to a boolean according to Scratch logic.
26    ///
27    /// | input             | associated boolean   |
28    /// | :---------------- | :------------------- |
29    /// | `""`              | `false`              |
30    /// | `"false"`         | `false`              |
31    /// | `"true"`          | `true`               |
32    /// | Int 0 / Float 0.0 | `false`              |
33    /// | Int 1 / Float 1.0 | `true`               |
34    /// | other string      | `true`               |
35    /// | other number      | `true`               |
36    /// | already boolean   | _unchanged_          |
37    ///
38    /// Occuring behaviour differences Scratch implements to avoid errors
39    /// will be reported to the sink.
40    ///
41    /// As expression blocks can't be placed in comparison contexts
42    /// these quriks should never occur because of a user's program.
43    ///
44    /// For completness they are reported.
45    pub fn q_as_bool<Q>(&self, sink: &mut Q) -> bool
46    where
47        Q: QuirkSink<SValueToBoolQ>,
48    {
49        match self {
50            Self::Text(t) => {
51                if t.as_ref() == "" {
52                    sink.put(SValueToBoolQ::EmptyStringTreatedAsFalse);
53                    false
54                } else if t.as_ref() == "false" {
55                    false
56                } else if t.as_ref() == "true" {
57                    true
58                } else {
59                    sink.put(SValueToBoolQ::NonEmptyStringTreatedAsTrue);
60                    true
61                }
62            }
63            Self::Bool(b) => *b,
64            Self::Int(0) | Self::Float(0.0) => {
65                sink.put(SValueToBoolQ::ZeroToFalse);
66                false
67            }
68            Self::Int(1) | Self::Float(1.0) => {
69                sink.put(SValueToBoolQ::OneToTrue);
70                true
71            }
72            Self::Int(i) => {
73                sink.put(SValueToBoolQ::IntToBoolNotLossless(*i));
74                true
75            }
76            Self::Float(f) => {
77                sink.put(SValueToBoolQ::FloatToBoolNotLossless(*f));
78                true
79            }
80        }
81    }
82}