triblespace_core/value/schemas/
range.rs1use crate::blob::schemas::longstring::LongString;
2use crate::id::ExclusiveId;
3use crate::id::Id;
4use crate::id_hex;
5use crate::macros::entity;
6use crate::metadata;
7use crate::metadata::ConstMetadata;
8use crate::repo::BlobStore;
9use crate::trible::TribleSet;
10use crate::value::schemas::hash::Blake3;
11use crate::value::FromValue;
12use crate::value::RawValue;
13use crate::value::ToValue;
14use crate::value::TryFromValue;
15use crate::value::TryToValue;
16use crate::value::Value;
17use crate::value::ValueSchema;
18use std::convert::Infallible;
19use std::ops::{Range, RangeInclusive};
20
21#[cfg(feature = "wasm")]
22use crate::blob::schemas::wasmcode::WasmCode;
23#[derive(Debug, Clone, Copy)]
30pub struct RangeU128;
31
32#[derive(Debug, Clone, Copy)]
33pub struct RangeInclusiveU128;
34
35impl ConstMetadata for RangeU128 {
36 fn id() -> Id {
37 id_hex!("A4E25E3B92364FA5AB519C6A77D7CB3A")
38 }
39
40 fn describe<B>(blobs: &mut B) -> Result<TribleSet, B::PutError>
41 where
42 B: BlobStore<Blake3>,
43 {
44 let id = Self::id();
45 let description = blobs.put::<LongString, _>(
46 "Half-open range encoded as two big-endian u128 values (start..end). This mirrors common slice semantics where the end is exclusive.\n\nUse for offsets, byte ranges, and spans where length matters and empty ranges are valid. Use RangeInclusiveU128 when both endpoints should be included.\n\nNo normalization is enforced; callers should ensure start <= end and interpret units consistently.",
47 )?;
48 let tribles = entity! {
49 ExclusiveId::force_ref(&id) @
50 metadata::shortname: "range_u128",
51 metadata::description: description,
52 metadata::tag: metadata::KIND_VALUE_SCHEMA,
53 };
54
55 #[cfg(feature = "wasm")]
56 let tribles = {
57 let mut tribles = tribles;
58 tribles += entity! { ExclusiveId::force_ref(&id) @
59 metadata::value_formatter: blobs.put::<WasmCode, _>(wasm_formatters::RANGE_U128_WASM)?,
60 };
61 tribles
62 };
63 Ok(tribles)
64 }
65}
66
67impl ValueSchema for RangeU128 {
68 type ValidationError = Infallible;
69}
70
71impl ConstMetadata for RangeInclusiveU128 {
72 fn id() -> Id {
73 id_hex!("1D0D82CA84424CD0A2F98DB37039E152")
74 }
75
76 fn describe<B>(blobs: &mut B) -> Result<TribleSet, B::PutError>
77 where
78 B: BlobStore<Blake3>,
79 {
80 let id = Self::id();
81 let description = blobs.put::<LongString, _>(
82 "Inclusive range encoded as two big-endian u128 values (start..=end). This is convenient when both endpoints are meaningful.\n\nUse for closed intervals such as line/column ranges or inclusive numeric bounds. Prefer RangeU128 for half-open intervals and length-based calculations.\n\nCallers should decide how to handle empty or reversed ranges; the schema only defines the byte layout.",
83 )?;
84 let tribles = entity! {
85 ExclusiveId::force_ref(&id) @
86 metadata::shortname: "range_u128_inc",
87 metadata::description: description,
88 metadata::tag: metadata::KIND_VALUE_SCHEMA,
89 };
90
91 #[cfg(feature = "wasm")]
92 let tribles = {
93 let mut tribles = tribles;
94 tribles += entity! { ExclusiveId::force_ref(&id) @
95 metadata::value_formatter: blobs.put::<WasmCode, _>(wasm_formatters::RANGE_INCLUSIVE_U128_WASM)?,
96 };
97 tribles
98 };
99 Ok(tribles)
100 }
101}
102
103#[cfg(feature = "wasm")]
104mod wasm_formatters {
105 use core::fmt::Write;
106
107 use triblespace_core_macros::value_formatter;
108
109 #[value_formatter]
110 pub(crate) fn range_u128(raw: &[u8; 32], out: &mut impl Write) -> Result<(), u32> {
111 let mut buf = [0u8; 16];
112 buf.copy_from_slice(&raw[..16]);
113 let start = u128::from_be_bytes(buf);
114 buf.copy_from_slice(&raw[16..]);
115 let end = u128::from_be_bytes(buf);
116 write!(out, "{start}..{end}").map_err(|_| 1u32)?;
117 Ok(())
118 }
119
120 #[value_formatter]
121 pub(crate) fn range_inclusive_u128(raw: &[u8; 32], out: &mut impl Write) -> Result<(), u32> {
122 let mut buf = [0u8; 16];
123 buf.copy_from_slice(&raw[..16]);
124 let start = u128::from_be_bytes(buf);
125 buf.copy_from_slice(&raw[16..]);
126 let end = u128::from_be_bytes(buf);
127 write!(out, "{start}..={end}").map_err(|_| 1u32)?;
128 Ok(())
129 }
130}
131
132impl ValueSchema for RangeInclusiveU128 {
133 type ValidationError = Infallible;
134}
135
136fn encode_pair(range: (u128, u128)) -> RawValue {
137 let mut raw = [0u8; 32];
138 raw[..16].copy_from_slice(&range.0.to_be_bytes());
139 raw[16..].copy_from_slice(&range.1.to_be_bytes());
140 raw
141}
142
143fn decode_pair(raw: &RawValue) -> (u128, u128) {
144 let mut first = [0u8; 16];
145 let mut second = [0u8; 16];
146 first.copy_from_slice(&raw[..16]);
147 second.copy_from_slice(&raw[16..]);
148 (u128::from_be_bytes(first), u128::from_be_bytes(second))
149}
150
151fn encode_range_value<S: ValueSchema>(range: (u128, u128)) -> Value<S> {
152 Value::new(encode_pair(range))
153}
154
155fn decode_range_value<S: ValueSchema>(value: &Value<S>) -> (u128, u128) {
156 decode_pair(&value.raw)
157}
158
159impl ToValue<RangeU128> for (u128, u128) {
160 fn to_value(self) -> Value<RangeU128> {
161 encode_range_value(self)
162 }
163}
164
165impl FromValue<'_, RangeU128> for (u128, u128) {
166 fn from_value(v: &Value<RangeU128>) -> Self {
167 decode_range_value(v)
168 }
169}
170
171impl ToValue<RangeInclusiveU128> for (u128, u128) {
172 fn to_value(self) -> Value<RangeInclusiveU128> {
173 encode_range_value(self)
174 }
175}
176
177impl FromValue<'_, RangeInclusiveU128> for (u128, u128) {
178 fn from_value(v: &Value<RangeInclusiveU128>) -> Self {
179 decode_range_value(v)
180 }
181}
182
183impl TryToValue<RangeU128> for Range<u128> {
184 type Error = Infallible;
185
186 fn try_to_value(self) -> Result<Value<RangeU128>, Self::Error> {
187 Ok(encode_range_value((self.start, self.end)))
188 }
189}
190
191impl TryFromValue<'_, RangeU128> for Range<u128> {
192 type Error = Infallible;
193
194 fn try_from_value(v: &Value<RangeU128>) -> Result<Self, Self::Error> {
195 let (start, end) = decode_range_value(v);
196 Ok(start..end)
197 }
198}
199
200impl TryToValue<RangeInclusiveU128> for RangeInclusive<u128> {
201 type Error = Infallible;
202
203 fn try_to_value(self) -> Result<Value<RangeInclusiveU128>, Self::Error> {
204 let (start, end) = self.into_inner();
205 Ok(encode_range_value((start, end)))
206 }
207}
208
209impl TryFromValue<'_, RangeInclusiveU128> for RangeInclusive<u128> {
210 type Error = Infallible;
211
212 fn try_from_value(v: &Value<RangeInclusiveU128>) -> Result<Self, Self::Error> {
213 let (start, end) = decode_range_value(v);
214 Ok(start..=end)
215 }
216}