triblespace_core/value/schemas/
linelocation.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::Value;
14use crate::value::ValueSchema;
15use proc_macro::Span;
16use std::convert::Infallible;
17
18#[cfg(feature = "wasm")]
19use crate::blob::schemas::wasmcode::WasmCode;
20#[derive(Debug, Clone, Copy)]
23pub struct LineLocation;
24
25impl ConstMetadata for LineLocation {
26 fn id() -> Id {
27 id_hex!("DFAED173A908498CB893A076EAD3E578")
28 }
29
30 fn describe<B>(blobs: &mut B) -> Result<TribleSet, B::PutError>
31 where
32 B: BlobStore<Blake3>,
33 {
34 let id = Self::id();
35 let description = blobs.put(
36 "Line/column span encoded as four big-endian u64 values (start_line, start_col, end_line, end_col). This captures explicit source positions rather than byte offsets.\n\nUse for editor diagnostics, source maps, or human-facing spans. If you need byte offsets or length-based ranges, use RangeU128 instead.\n\nColumns are raw counts and do not account for variable-width graphemes. Store any display conventions separately if needed.",
37 )?;
38 let tribles = entity! {
39 ExclusiveId::force_ref(&id) @
40 metadata::name: blobs.put("line_location".to_string())?,
41 metadata::description: description,
42 metadata::tag: metadata::KIND_VALUE_SCHEMA,
43 };
44
45 #[cfg(feature = "wasm")]
46 let tribles = {
47 let mut tribles = tribles;
48 tribles += entity! { ExclusiveId::force_ref(&id) @
49 metadata::value_formatter: blobs.put(wasm_formatter::LINELOCATION_WASM)?,
50 };
51 tribles
52 };
53 Ok(tribles)
54 }
55}
56
57#[cfg(feature = "wasm")]
58mod wasm_formatter {
59 use core::fmt::Write;
60
61 use triblespace_core_macros::value_formatter;
62
63 #[value_formatter]
64 pub(crate) fn linelocation(raw: &[u8; 32], out: &mut impl Write) -> Result<(), u32> {
65 let mut buf = [0u8; 8];
66 buf.copy_from_slice(&raw[..8]);
67 let start_line = u64::from_be_bytes(buf);
68 buf.copy_from_slice(&raw[8..16]);
69 let start_col = u64::from_be_bytes(buf);
70 buf.copy_from_slice(&raw[16..24]);
71 let end_line = u64::from_be_bytes(buf);
72 buf.copy_from_slice(&raw[24..]);
73 let end_col = u64::from_be_bytes(buf);
74
75 write!(out, "{start_line}:{start_col}..{end_line}:{end_col}").map_err(|_| 1u32)?;
76 Ok(())
77 }
78}
79
80impl ValueSchema for LineLocation {
81 type ValidationError = Infallible;
82}
83
84fn encode_location(lines: (u64, u64, u64, u64)) -> RawValue {
85 let mut raw = [0u8; 32];
86 raw[..8].copy_from_slice(&lines.0.to_be_bytes());
87 raw[8..16].copy_from_slice(&lines.1.to_be_bytes());
88 raw[16..24].copy_from_slice(&lines.2.to_be_bytes());
89 raw[24..].copy_from_slice(&lines.3.to_be_bytes());
90 raw
91}
92
93fn decode_location(raw: &RawValue) -> (u64, u64, u64, u64) {
94 let mut first = [0u8; 8];
95 let mut second = [0u8; 8];
96 let mut third = [0u8; 8];
97 let mut fourth = [0u8; 8];
98 first.copy_from_slice(&raw[..8]);
99 second.copy_from_slice(&raw[8..16]);
100 third.copy_from_slice(&raw[16..24]);
101 fourth.copy_from_slice(&raw[24..]);
102 (
103 u64::from_be_bytes(first),
104 u64::from_be_bytes(second),
105 u64::from_be_bytes(third),
106 u64::from_be_bytes(fourth),
107 )
108}
109
110impl ToValue<LineLocation> for (u64, u64, u64, u64) {
111 fn to_value(self) -> Value<LineLocation> {
112 Value::new(encode_location(self))
113 }
114}
115
116impl FromValue<'_, LineLocation> for (u64, u64, u64, u64) {
117 fn from_value(v: &Value<LineLocation>) -> Self {
118 decode_location(&v.raw)
119 }
120}
121
122impl ToValue<LineLocation> for Span {
123 fn to_value(self) -> Value<LineLocation> {
124 (
125 self.start().line() as u64,
126 self.start().column() as u64,
127 self.end().line() as u64,
128 self.end().column() as u64,
129 )
130 .to_value()
131 }
132}