triblespace_core/inline/encodings/
boolean.rs1use crate::inline::Encodes;
2use crate::id::ExclusiveId;
3use crate::id::Id;
4use crate::id_hex;
5use crate::macros::entity;
6use crate::metadata;
7use crate::metadata::MetaDescribe;
8use crate::trible::Fragment;
9use crate::inline::TryFromInline;
10use crate::inline::TryToInline;
11use crate::inline::Inline;
12use crate::inline::InlineEncoding;
13use crate::inline::INLINE_LEN;
14
15use std::convert::Infallible;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub struct InvalidBoolean;
20
21pub struct Boolean;
27
28impl Boolean {
29 fn encode(flag: bool) -> Inline<Self> {
30 if flag {
31 Inline::new([u8::MAX; INLINE_LEN])
32 } else {
33 Inline::new([0u8; INLINE_LEN])
34 }
35 }
36
37 fn decode(value: &Inline<Self>) -> Result<bool, InvalidBoolean> {
38 if value.raw.iter().all(|&b| b == 0) {
39 Ok(false)
40 } else if value.raw.iter().all(|&b| b == u8::MAX) {
41 Ok(true)
42 } else {
43 Err(InvalidBoolean)
44 }
45 }
46}
47
48impl MetaDescribe for Boolean {
49 fn describe() -> Fragment {
50 let id: Id = id_hex!("73B414A3E25B0C0F9E4D6B0694DC33C5");
51 #[allow(unused_mut)]
52 let mut tribles = entity! {
53 ExclusiveId::force_ref(&id) @
54 metadata::name: "boolean",
55 metadata::description: "Boolean stored as all-zero bytes for false and all-0xFF bytes for true. The encoding uses the full 32-byte value, making the two states obvious and cheap to test.\n\nUse for simple flags and binary states. Represent unknown or missing data by omitting the trible rather than inventing a third sentinel value.\n\nMixed patterns are invalid and will fail validation. If you need tri-state or richer states, model it explicitly (for example with ShortString or a dedicated entity).",
56 metadata::tag: metadata::KIND_INLINE_ENCODING,
57 };
58
59 #[cfg(feature = "wasm")]
60 {
61 tribles += entity! { ExclusiveId::force_ref(&id) @
62 metadata::value_formatter: wasm_formatter::BOOLEAN_WASM,
63 };
64 }
65 tribles
66 }
67}
68
69#[cfg(feature = "wasm")]
70mod wasm_formatter {
71 use core::fmt::Write;
72
73 use triblespace_core_macros::value_formatter;
74
75 #[value_formatter]
76 pub(crate) fn boolean(raw: &[u8; 32], out: &mut impl Write) -> Result<(), u32> {
77 let all_zero = raw.iter().all(|&b| b == 0);
78 let all_ones = raw.iter().all(|&b| b == u8::MAX);
79
80 let text = if all_zero {
81 "false"
82 } else if all_ones {
83 "true"
84 } else {
85 return Err(2);
86 };
87
88 out.write_str(text).map_err(|_| 1u32)?;
89 Ok(())
90 }
91}
92
93impl InlineEncoding for Boolean {
94 type ValidationError = InvalidBoolean;
95 type Encoding = Self;
96
97 fn validate(value: Inline<Self>) -> Result<Inline<Self>, Self::ValidationError> {
98 Self::decode(&value)?;
99 Ok(value)
100 }
101}
102
103impl<'a> TryFromInline<'a, Boolean> for bool {
104 type Error = InvalidBoolean;
105
106 fn try_from_inline(v: &'a Inline<Boolean>) -> Result<Self, Self::Error> {
107 Boolean::decode(v)
108 }
109}
110
111impl TryToInline<Boolean> for bool {
112 type Error = Infallible;
113
114 fn try_to_inline(self) -> Result<Inline<Boolean>, Self::Error> {
115 Ok(Boolean::encode(self))
116 }
117}
118
119impl TryToInline<Boolean> for &bool {
120 type Error = Infallible;
121
122 fn try_to_inline(self) -> Result<Inline<Boolean>, Self::Error> {
123 Ok(Boolean::encode(*self))
124 }
125}
126
127impl Encodes<bool> for Boolean
128{
129 type Output = Inline<Boolean>;
130 fn encode(source: bool) -> Inline<Boolean> {
131 Boolean::encode(source)
132 }
133}
134
135impl Encodes<&bool> for Boolean
136{
137 type Output = Inline<Boolean>;
138 fn encode(source: &bool) -> Inline<Boolean> {
139 Boolean::encode(*source)
140 }
141}
142
143#[cfg(test)]
144mod tests {
145 use super::Boolean;
146 use super::InvalidBoolean;
147 use crate::inline::Inline;
148 use crate::inline::InlineEncoding;
149
150 #[test]
151 fn encodes_false_as_zero_bytes() {
152 let value = Boolean::inline_from(false);
153 assert!(value.raw.iter().all(|&b| b == 0));
154 assert_eq!(Boolean::validate(value), Ok(Boolean::inline_from(false)));
155 }
156
157 #[test]
158 fn encodes_true_as_all_ones() {
159 let value = Boolean::inline_from(true);
160 assert!(value.raw.iter().all(|&b| b == u8::MAX));
161 assert_eq!(Boolean::validate(value), Ok(Boolean::inline_from(true)));
162 }
163
164 #[test]
165 fn rejects_mixed_bit_patterns() {
166 let mut mixed = [0u8; crate::inline::INLINE_LEN];
167 mixed[0] = 1;
168 let value = Inline::<Boolean>::new(mixed);
169 assert_eq!(Boolean::validate(value), Err(InvalidBoolean));
170 }
171}