triblespace_core/value/schemas/
range.rs1use crate::id::ExclusiveId;
2use crate::id::Id;
3use crate::id_hex;
4use crate::macros::entity;
5use crate::metadata;
6use crate::metadata::{ConstDescribe, ConstId};
7use crate::repo::BlobStore;
8use crate::trible::Fragment;
9use crate::value::schemas::hash::Blake3;
10use crate::value::RawValue;
11use crate::value::ToValue;
12use crate::value::TryFromValue;
13use crate::value::TryToValue;
14use crate::value::Value;
15use crate::value::ValueSchema;
16use std::convert::Infallible;
17use std::ops::{Range, RangeInclusive};
18
19#[derive(Debug, Clone, Copy)]
26pub struct RangeU128;
27
28#[derive(Debug, Clone, Copy)]
30pub struct RangeInclusiveU128;
31
32impl ConstId for RangeU128 {
33 const ID: Id = id_hex!("A4E25E3B92364FA5AB519C6A77D7CB3A");
34}
35
36impl ConstId for RangeInclusiveU128 {
37 const ID: Id = id_hex!("1D0D82CA84424CD0A2F98DB37039E152");
38}
39
40impl ConstDescribe for RangeU128 {
41 fn describe<B>(blobs: &mut B) -> Result<Fragment, B::PutError>
42 where
43 B: BlobStore<Blake3>,
44 {
45 let id = Self::ID;
46 let description = blobs.put(
47 "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.",
48 )?;
49 let tribles = entity! {
50 ExclusiveId::force_ref(&id) @
51 metadata::name: blobs.put("range_u128")?,
52 metadata::description: description,
53 metadata::tag: metadata::KIND_VALUE_SCHEMA,
54 };
55
56 #[cfg(feature = "wasm")]
57 let tribles = {
58 let mut tribles = tribles;
59 tribles += entity! { ExclusiveId::force_ref(&id) @
60 metadata::value_formatter: blobs.put(wasm_formatters::RANGE_U128_WASM)?,
61 };
62 tribles
63 };
64 Ok(tribles)
65 }
66}
67
68impl ValueSchema for RangeU128 {
69 type ValidationError = Infallible;
70}
71
72impl ConstDescribe for RangeInclusiveU128 {
73 fn describe<B>(blobs: &mut B) -> Result<Fragment, B::PutError>
74 where
75 B: BlobStore<Blake3>,
76 {
77 let id = Self::ID;
78 let description = blobs.put(
79 "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.",
80 )?;
81 let tribles = entity! {
82 ExclusiveId::force_ref(&id) @
83 metadata::name: blobs.put("range_u128_inc")?,
84 metadata::description: description,
85 metadata::tag: metadata::KIND_VALUE_SCHEMA,
86 };
87
88 #[cfg(feature = "wasm")]
89 let tribles = {
90 let mut tribles = tribles;
91 tribles += entity! { ExclusiveId::force_ref(&id) @
92 metadata::value_formatter: blobs.put(wasm_formatters::RANGE_INCLUSIVE_U128_WASM)?,
93 };
94 tribles
95 };
96 Ok(tribles)
97 }
98}
99
100#[cfg(feature = "wasm")]
101mod wasm_formatters {
102 use core::fmt::Write;
103
104 use triblespace_core_macros::value_formatter;
105
106 #[value_formatter]
107 pub(crate) fn range_u128(raw: &[u8; 32], out: &mut impl Write) -> Result<(), u32> {
108 let mut buf = [0u8; 16];
109 buf.copy_from_slice(&raw[..16]);
110 let start = u128::from_be_bytes(buf);
111 buf.copy_from_slice(&raw[16..]);
112 let end = u128::from_be_bytes(buf);
113 write!(out, "{start}..{end}").map_err(|_| 1u32)?;
114 Ok(())
115 }
116
117 #[value_formatter]
118 pub(crate) fn range_inclusive_u128(raw: &[u8; 32], out: &mut impl Write) -> Result<(), u32> {
119 let mut buf = [0u8; 16];
120 buf.copy_from_slice(&raw[..16]);
121 let start = u128::from_be_bytes(buf);
122 buf.copy_from_slice(&raw[16..]);
123 let end = u128::from_be_bytes(buf);
124 write!(out, "{start}..={end}").map_err(|_| 1u32)?;
125 Ok(())
126 }
127}
128
129impl ValueSchema for RangeInclusiveU128 {
130 type ValidationError = Infallible;
131}
132
133fn encode_pair(range: (u128, u128)) -> RawValue {
134 let mut raw = [0u8; 32];
135 raw[..16].copy_from_slice(&range.0.to_be_bytes());
136 raw[16..].copy_from_slice(&range.1.to_be_bytes());
137 raw
138}
139
140fn decode_pair(raw: &RawValue) -> (u128, u128) {
141 let mut first = [0u8; 16];
142 let mut second = [0u8; 16];
143 first.copy_from_slice(&raw[..16]);
144 second.copy_from_slice(&raw[16..]);
145 (u128::from_be_bytes(first), u128::from_be_bytes(second))
146}
147
148fn encode_range_value<S: ValueSchema>(range: (u128, u128)) -> Value<S> {
149 Value::new(encode_pair(range))
150}
151
152fn decode_range_value<S: ValueSchema>(value: &Value<S>) -> (u128, u128) {
153 decode_pair(&value.raw)
154}
155
156impl ToValue<RangeU128> for (u128, u128) {
157 fn to_value(self) -> Value<RangeU128> {
158 encode_range_value(self)
159 }
160}
161
162impl TryFromValue<'_, RangeU128> for (u128, u128) {
163 type Error = Infallible;
164 fn try_from_value(v: &Value<RangeU128>) -> Result<Self, Infallible> {
165 Ok(decode_range_value(v))
166 }
167}
168
169impl ToValue<RangeInclusiveU128> for (u128, u128) {
170 fn to_value(self) -> Value<RangeInclusiveU128> {
171 encode_range_value(self)
172 }
173}
174
175impl TryFromValue<'_, RangeInclusiveU128> for (u128, u128) {
176 type Error = Infallible;
177 fn try_from_value(v: &Value<RangeInclusiveU128>) -> Result<Self, Infallible> {
178 Ok(decode_range_value(v))
179 }
180}
181
182impl TryToValue<RangeU128> for Range<u128> {
183 type Error = Infallible;
184
185 fn try_to_value(self) -> Result<Value<RangeU128>, Self::Error> {
186 Ok(encode_range_value((self.start, self.end)))
187 }
188}
189
190impl TryFromValue<'_, RangeU128> for Range<u128> {
191 type Error = Infallible;
192
193 fn try_from_value(v: &Value<RangeU128>) -> Result<Self, Self::Error> {
194 let (start, end) = decode_range_value(v);
195 Ok(start..end)
196 }
197}
198
199impl TryToValue<RangeInclusiveU128> for RangeInclusive<u128> {
200 type Error = Infallible;
201
202 fn try_to_value(self) -> Result<Value<RangeInclusiveU128>, Self::Error> {
203 let (start, end) = self.into_inner();
204 Ok(encode_range_value((start, end)))
205 }
206}
207
208impl TryFromValue<'_, RangeInclusiveU128> for RangeInclusive<u128> {
209 type Error = Infallible;
210
211 fn try_from_value(v: &Value<RangeInclusiveU128>) -> Result<Self, Self::Error> {
212 let (start, end) = decode_range_value(v);
213 Ok(start..=end)
214 }
215}