Skip to main content

qualia_core_db/query/
rdf_star.rs

1//! RDF-Star Parsing and Serialization Infrastructure
2//!
3//! This module provides trait definitions and shared infrastructure for
4//! parsing and serializing RDF-Star (SPARQL 1.2) data across multiple formats.
5//!
6//! All parsers converge on Virtual IDs (TAG_EMBEDDED | 60-bit hash).
7//! All serializers diverge from Virtual IDs to format-specific syntax.
8
9use crate::lexicon::{generate_embedded_triple_id, TAG_EMBEDDED};
10
11/// Error type for RDF-Star parsing operations
12#[derive(Debug, Clone, PartialEq, Eq)]
13pub enum RdfStarParseError {
14    /// Invalid syntax for the format
15    InvalidSyntax,
16    /// Malformed embedded triple
17    MalformedEmbeddedTriple,
18    /// Buffer overflow during parsing
19    BufferOverflow,
20    /// Unsupported feature for this format
21    UnsupportedFeature,
22    /// Invalid UTF-8 encoding
23    InvalidUtf8,
24    /// Lexicon lookup failed
25    LexiconError,
26}
27
28/// Error type for RDF-Star serialization operations
29#[derive(Debug, Clone, PartialEq, Eq)]
30pub enum RdfStarSerializeError {
31    /// Virtual ID not found in lexicon
32    VirtualIdNotFound,
33    /// Component IDs not found in lexicon
34    ComponentNotFound,
35    /// Buffer too small for output
36    BufferTooSmall,
37    /// Unsupported feature for this format
38    UnsupportedFeature,
39}
40
41impl std::fmt::Display for RdfStarParseError {
42    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43        write!(f, "{:?}", self)
44    }
45}
46impl std::error::Error for RdfStarParseError {}
47
48impl std::fmt::Display for RdfStarSerializeError {
49    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50        write!(f, "{:?}", self)
51    }
52}
53impl std::error::Error for RdfStarSerializeError {}
54
55/// Trait for RDF-Star parsers across multiple formats
56///
57/// All parsers must implement this trait to ensure consistent
58/// conversion from format-specific syntax to Virtual IDs.
59pub trait RdfStarParser {
60    /// Parse an embedded triple from format-specific syntax
61    ///
62    /// Returns (Virtual ID, [subject_id, predicate_id, object_id])
63    fn parse_embedded_triple(&mut self, input: &[u8])
64        -> Result<(u64, [u64; 3]), RdfStarParseError>;
65
66    /// Parse a regular triple (subject, predicate, object)
67    fn parse_triple(&mut self, input: &[u8]) -> Result<(u64, u64, u64), RdfStarParseError>;
68
69    /// Parse a quad (subject, predicate, object, graph)
70    ///
71    /// Returns None if the format doesn't support quads
72    fn parse_quad(&mut self, input: &[u8]) -> Result<(u64, u64, u64, u64), RdfStarParseError>;
73
74    /// Check if this parser supports quad formats (named graphs)
75    fn supports_quads(&self) -> bool;
76
77    /// Check if this parser supports named graphs
78    fn supports_named_graphs(&self) -> bool;
79
80    /// Get the format name for error reporting
81    fn format_name(&self) -> &'static str;
82}
83
84/// Trait for RDF-Star serializers across multiple formats
85///
86/// All serializers must implement this trait to ensure consistent
87/// conversion from Virtual IDs to format-specific syntax.
88pub trait RdfStarSerializer {
89    /// Serialize a Virtual ID back to format-specific embedded triple syntax
90    ///
91    /// Takes the Virtual ID and its component IDs (retrieved from lexicon)
92    fn serialize_embedded_triple(
93        &self,
94        virtual_id: u64,
95        components: &[u64; 3],
96    ) -> Result<Vec<u8>, RdfStarSerializeError>;
97
98    /// Serialize a regular triple
99    fn serialize_triple(
100        &self,
101        subject: u64,
102        predicate: u64,
103        object: u64,
104    ) -> Result<Vec<u8>, RdfStarSerializeError>;
105
106    /// Serialize a quad (subject, predicate, object, graph)
107    fn serialize_quad(
108        &self,
109        subject: u64,
110        predicate: u64,
111        object: u64,
112        graph: u64,
113    ) -> Result<Vec<u8>, RdfStarSerializeError>;
114
115    /// Check if this serializer supports quad formats
116    fn supports_quads(&self) -> bool;
117
118    /// Get the format name for error reporting
119    fn format_name(&self) -> &'static str;
120}
121
122/// Helper function to create a Virtual ID from component IDs
123///
124/// This is a convenience wrapper around generate_embedded_triple_id
125/// that also validates the TAG_EMBEDDED bit is set correctly.
126#[inline(always)]
127pub fn create_virtual_id(subject: u64, predicate: u64, object: u64) -> u64 {
128    let virtual_id = generate_embedded_triple_id(subject, predicate, object);
129    debug_assert_eq!(
130        virtual_id & TAG_EMBEDDED,
131        TAG_EMBEDDED,
132        "TAG_EMBEDDED bit should be set"
133    );
134    virtual_id
135}
136
137/// Check if a u64 is a Virtual ID (has TAG_EMBEDDED bit set)
138#[inline(always)]
139pub fn is_virtual_id(value: u64) -> bool {
140    (value & TAG_EMBEDDED) != 0
141}
142
143#[cfg(test)]
144mod tests {
145    use super::*;
146
147    #[test]
148    fn test_create_virtual_id_sets_tag() {
149        let vid = create_virtual_id(1, 2, 3);
150        assert_eq!(vid & TAG_EMBEDDED, TAG_EMBEDDED);
151    }
152
153    #[test]
154    fn test_is_virtual_id() {
155        let vid = create_virtual_id(1, 2, 3);
156        assert!(is_virtual_id(vid));
157
158        let regular_hash = 12345u64;
159        assert!(!is_virtual_id(regular_hash));
160    }
161
162    #[test]
163    fn test_create_virtual_id_deterministic() {
164        let vid1 = create_virtual_id(1, 2, 3);
165        let vid2 = create_virtual_id(1, 2, 3);
166        assert_eq!(vid1, vid2);
167    }
168}
169
170/// SPARQL-Star BIND Functions
171///
172/// These functions are used in SPARQL queries to extract components from
173/// embedded triples using the BIND keyword.
174///
175/// Example: BIND (SUBJECT(?triple) AS ?subject)
176
177/// Extract the subject component from an embedded triple.
178///
179/// Given a Virtual ID, performs a binary search in the Q42LEX data and returns
180/// the subject component ID stored in the embedded triple entry.
181pub fn subject_of_virtual_id(
182    virtual_id: u64,
183    lex_data: &[u8],
184    _blob_base: usize,
185) -> Result<u64, RdfStarParseError> {
186    if !is_virtual_id(virtual_id) {
187        return Err(RdfStarParseError::MalformedEmbeddedTriple);
188    }
189    let lex = crate::q42_lex::Q42LexMmap::from_bytes(lex_data)
190        .map_err(|_| RdfStarParseError::LexiconError)?;
191    lex.lookup_embedded_triple(virtual_id)
192        .map(|t| t[0])
193        .ok_or(RdfStarParseError::LexiconError)
194}
195
196/// Extract the predicate component from an embedded triple.
197pub fn predicate_of_virtual_id(
198    virtual_id: u64,
199    lex_data: &[u8],
200    _blob_base: usize,
201) -> Result<u64, RdfStarParseError> {
202    if !is_virtual_id(virtual_id) {
203        return Err(RdfStarParseError::MalformedEmbeddedTriple);
204    }
205    let lex = crate::q42_lex::Q42LexMmap::from_bytes(lex_data)
206        .map_err(|_| RdfStarParseError::LexiconError)?;
207    lex.lookup_embedded_triple(virtual_id)
208        .map(|t| t[1])
209        .ok_or(RdfStarParseError::LexiconError)
210}
211
212/// Extract the object component from an embedded triple.
213pub fn object_of_virtual_id(
214    virtual_id: u64,
215    lex_data: &[u8],
216    _blob_base: usize,
217) -> Result<u64, RdfStarParseError> {
218    if !is_virtual_id(virtual_id) {
219        return Err(RdfStarParseError::MalformedEmbeddedTriple);
220    }
221    let lex = crate::q42_lex::Q42LexMmap::from_bytes(lex_data)
222        .map_err(|_| RdfStarParseError::LexiconError)?;
223    lex.lookup_embedded_triple(virtual_id)
224        .map(|t| t[2])
225        .ok_or(RdfStarParseError::LexiconError)
226}
227
228/// Construct a Virtual ID from three component IDs
229///
230/// This is the inverse of the extraction functions.
231pub fn triple_from_components(subject: u64, predicate: u64, object: u64) -> u64 {
232    create_virtual_id(subject, predicate, object)
233}