Skip to main content

qualia_core_db/query/
cbor_compiler.rs

1use crate::NQuin;
2
3#[derive(Debug, PartialEq, Eq)]
4pub enum ParseError {
5    UnsupportedMediaType, // HTTP 415 equivalent
6    MalformedPayload,
7    BufferOverflow,
8}
9
10/// The Strict Binary Gatekeeper for Qualia-DB network payloads.
11/// Explicitly rejects text-based semantic payloads and enforces binary
12/// CBOR-framed ingestion, with the intended semantic payload profile being
13/// CBOR-LD.
14pub fn ingest_network_payload(payload: &[u8]) -> Result<&[u8], ParseError> {
15    if payload.is_empty() {
16        return Err(ParseError::MalformedPayload);
17    }
18
19    // Read payload[0] to determine the encoding format.
20    let first_byte = payload[0];
21
22    // Reject common text-based semantic web formats:
23    // b'{' (0x7B) -> JSON-LD / JSON
24    // b'<' (0x3C) -> XML / RDF/XML
25    // b'@' (0x40) -> Turtle / N3 prefixes
26    if first_byte == b'{' || first_byte == b'<' || first_byte == b'@' {
27        return Err(ParseError::UnsupportedMediaType);
28    }
29
30    // CBOR Map (0xA0 to 0xB7) or CBOR Array (0x80 to 0x97) headers
31    // If it falls outside standard CBOR composite boundaries (for a root object), we reject it.
32    let is_cbor_map = (0xA0..=0xB7).contains(&first_byte);
33    let is_cbor_array = (0x80..=0x97).contains(&first_byte);
34    let is_cbor_indefinite = first_byte == 0xBF || first_byte == 0x9F;
35
36    if is_cbor_map || is_cbor_array || is_cbor_indefinite {
37        Ok(payload)
38    } else {
39        Err(ParseError::UnsupportedMediaType)
40    }
41}
42
43/// A lightweight, no_std compatible loop to read variable-length integers from CBOR-LD.
44/// Maps the extracted dictionary tags directly into the 64-bit Lexicon registers
45/// without allocating a single String or Vec.
46pub fn parse_cbor_ld_to_quin(payload: &[u8]) -> Result<NQuin, ParseError> {
47    // 1. Enforce the Strict Binary Gatekeeper
48    let valid_payload = ingest_network_payload(payload)?;
49
50    let mut cursor = 1; // Skip the root map/array byte
51
52    // Helper closure to safely read variable-length CBOR integers
53    let mut read_cbor_int = || -> Result<u64, ParseError> {
54        if cursor >= valid_payload.len() {
55            return Err(ParseError::BufferOverflow);
56        }
57        let byte = valid_payload[cursor];
58        cursor += 1;
59
60        let major_type = byte >> 5;
61        // Only accept unsigned integers (major type 0) for Lexicon tags
62        if major_type != 0 {
63            // In a full implementation, we'd handle tags and strings.
64            // For the Strict Binary Dictionary, all URIs are pre-compressed to integers.
65            return Ok(0);
66        }
67
68        let additional_info = byte & 0x1F;
69        match additional_info {
70            0..=23 => Ok(additional_info as u64),
71            24 => {
72                if cursor + 1 > valid_payload.len() {
73                    return Err(ParseError::BufferOverflow);
74                }
75                let val = valid_payload[cursor] as u64;
76                cursor += 1;
77                Ok(val)
78            }
79            25 => {
80                if cursor + 2 > valid_payload.len() {
81                    return Err(ParseError::BufferOverflow);
82                }
83                let mut bytes = [0u8; 2];
84                bytes.copy_from_slice(&valid_payload[cursor..cursor + 2]);
85                cursor += 2;
86                Ok(u16::from_be_bytes(bytes) as u64)
87            }
88            26 => {
89                if cursor + 4 > valid_payload.len() {
90                    return Err(ParseError::BufferOverflow);
91                }
92                let mut bytes = [0u8; 4];
93                bytes.copy_from_slice(&valid_payload[cursor..cursor + 4]);
94                cursor += 4;
95                Ok(u32::from_be_bytes(bytes) as u64)
96            }
97            27 => {
98                if cursor + 8 > valid_payload.len() {
99                    return Err(ParseError::BufferOverflow);
100                }
101                let mut bytes = [0u8; 8];
102                bytes.copy_from_slice(&valid_payload[cursor..cursor + 8]);
103                cursor += 8;
104                Ok(u64::from_be_bytes(bytes))
105            }
106            _ => Err(ParseError::MalformedPayload),
107        }
108    };
109
110    // We assume the CBOR-LD payload is an array of 4 integers: [Subject, Predicate, Object, Context]
111    // representing the dictionary-compressed Lexicon tags.
112    let subject = read_cbor_int()?;
113    let predicate = read_cbor_int()?;
114    let object = read_cbor_int()?;
115    let context = read_cbor_int()?;
116
117    // Hardcode metadata to Passthrough for this base compilation layer
118    let metadata = 0b00 << 61;
119
120    Ok(NQuin {
121        subject,
122        predicate,
123        object,
124        context,
125        metadata,
126        parity: 0, // In production, ECC checksum calculated here
127    })
128}
129
130#[cfg(test)]
131mod tests {
132    use super::*;
133
134    #[test]
135    fn test_gatekeeper_rejects_json() {
136        let json_payload = b"{\"@context\": \"http://schema.org\"}";
137        assert_eq!(
138            ingest_network_payload(json_payload),
139            Err(ParseError::UnsupportedMediaType)
140        );
141    }
142
143    #[test]
144    fn test_gatekeeper_rejects_turtle() {
145        let turtle_payload = b"@prefix qualia: <urn:qualia:> .";
146        assert_eq!(
147            ingest_network_payload(turtle_payload),
148            Err(ParseError::UnsupportedMediaType)
149        );
150    }
151
152    #[test]
153    fn test_parse_cbor_ld_dictionary() {
154        // CBOR Array of 4 integers: [1000, 2000, 3000, 4000]
155        // Array header: 0x84
156        // 1000 = 0x19 0x03 0xE8
157        // 2000 = 0x19 0x07 0xD0
158        // 3000 = 0x19 0x0B 0xB8
159        // 4000 = 0x19 0x0F 0xA0
160        let cbor_payload: [u8; 13] = [
161            0x84, 0x19, 0x03, 0xE8, 0x19, 0x07, 0xD0, 0x19, 0x0B, 0xB8, 0x19, 0x0F, 0xA0,
162        ];
163
164        let quin = parse_cbor_ld_to_quin(&cbor_payload).unwrap();
165
166        assert_eq!(quin.subject, 1000);
167        assert_eq!(quin.predicate, 2000);
168        assert_eq!(quin.object, 3000);
169        assert_eq!(quin.context, 4000);
170    }
171
172    #[test]
173    fn test_parse_cbor_buffer_overflow() {
174        // Truncated array header
175        let cbor_payload: [u8; 4] = [0x84, 0x19, 0x03, 0xE8];
176        assert_eq!(
177            parse_cbor_ld_to_quin(&cbor_payload),
178            Err(ParseError::BufferOverflow)
179        );
180    }
181}