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