1use crate::lexicon::{generate_60bit_token, generate_embedded_triple_id};
13use crate::rdf_star::{RdfStarParseError, RdfStarParser};
14use crate::NQuin;
15use std::io::{BufRead, BufReader, Read};
16
17const MAX_NESTING_DEPTH: usize = 16;
19
20#[repr(C)]
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23enum ParsingState {
24 ExpectSubject,
25 ExpectPredicate,
26 ExpectObject,
27 ExpectEmbeddedEnd,
28}
29
30#[repr(C)]
32#[derive(Debug, Clone, Copy)]
33struct StackFrame {
34 subject: Option<u64>,
35 predicate: Option<u64>,
36 object: Option<u64>,
37 parsing_state: ParsingState,
38}
39
40impl StackFrame {
41 fn new() -> Self {
42 Self {
43 subject: None,
44 predicate: None,
45 object: None,
46 parsing_state: ParsingState::ExpectSubject,
47 }
48 }
49}
50
51#[repr(C)]
53struct ParserStack {
54 frames: [StackFrame; MAX_NESTING_DEPTH],
55 depth: usize,
56}
57
58impl ParserStack {
59 fn new() -> Self {
60 Self {
61 frames: [StackFrame::new(); MAX_NESTING_DEPTH],
62 depth: 0,
63 }
64 }
65
66 fn push(&mut self, frame: StackFrame) -> Result<(), RdfStarParseError> {
67 if self.depth >= MAX_NESTING_DEPTH {
68 return Err(RdfStarParseError::BufferOverflow);
69 }
70 self.frames[self.depth] = frame;
71 self.depth += 1;
72 Ok(())
73 }
74
75 fn pop(&mut self) -> Option<StackFrame> {
76 if self.depth == 0 {
77 return None;
78 }
79 self.depth -= 1;
80 Some(self.frames[self.depth])
81 }
82
83 fn current(&mut self) -> &mut StackFrame {
84 &mut self.frames[self.depth - 1]
85 }
86
87 fn is_empty(&self) -> bool {
88 self.depth == 0
89 }
90
91 fn depth(&self) -> usize {
92 self.depth
93 }
94}
95
96pub struct TurtleStarParser {
98 context_hash: u64,
100 stack: ParserStack,
102}
103
104impl TurtleStarParser {
105 pub fn new(context_hash: u64) -> Self {
107 Self {
108 context_hash,
109 stack: ParserStack::new(),
110 }
111 }
112
113 pub fn session_context(&self) -> u64 {
115 self.context_hash
116 }
117
118 pub fn stack_is_empty(&self) -> bool {
120 self.stack.is_empty()
121 }
122
123 fn expect_state(
124 frame: &mut StackFrame,
125 expected: ParsingState,
126 next: ParsingState,
127 ) -> Result<(), RdfStarParseError> {
128 if frame.parsing_state != expected {
129 return Err(RdfStarParseError::MalformedEmbeddedTriple);
130 }
131 frame.parsing_state = next;
132 Ok(())
133 }
134
135 fn parse_token(&self, input: &[u8], pos: &mut usize) -> Result<Option<u64>, RdfStarParseError> {
139 let bytes = &input[*pos..];
140
141 let mut start = 0;
143 while start < bytes.len() && bytes[start].is_ascii_whitespace() {
144 start += 1;
145 }
146
147 if start >= bytes.len() {
148 return Ok(None);
149 }
150
151 let ch = bytes[start];
152
153 if ch == b'<' && start + 1 < bytes.len() && bytes[start + 1] == b'<' {
155 *pos += start + 2;
156 return Ok(None); }
158
159 if ch == b'>' && start + 1 < bytes.len() && bytes[start + 1] == b'>' {
161 *pos += start + 2;
162 return Ok(None); }
164
165 if ch == b'.' {
167 *pos += start + 1;
168 return Ok(None);
169 }
170
171 if ch == b';' {
173 *pos += start + 1;
174 return Ok(None);
175 }
176
177 let mut end = start;
179 while end < bytes.len()
180 && !bytes[end].is_ascii_whitespace()
181 && bytes[end] != b'.'
182 && bytes[end] != b';'
183 {
184 end += 1;
185 }
186
187 if start == end {
188 return Ok(None);
189 }
190
191 *pos += end;
192
193 let token =
195 std::str::from_utf8(&bytes[start..end]).map_err(|_| RdfStarParseError::InvalidUtf8)?;
196 let hash = generate_60bit_token(token.as_bytes());
197
198 Ok(Some(hash))
199 }
200
201 fn parse_embedded_triple_internal(
203 &mut self,
204 input: &[u8],
205 pos: &mut usize,
206 ) -> Result<(u64, [u64; 3]), RdfStarParseError> {
207 let frame = StackFrame::new();
209 self.stack.push(frame)?;
210
211 let _start_depth = self.stack.depth();
212
213 {
215 let frame = self.stack.current();
216 Self::expect_state(
217 frame,
218 ParsingState::ExpectSubject,
219 ParsingState::ExpectPredicate,
220 )?;
221 }
222 while *pos < input.len() && input[*pos].is_ascii_whitespace() {
223 *pos += 1;
224 }
225 if *pos + 1 < input.len() && input[*pos] == b'<' && input[*pos + 1] == b'<' {
226 *pos += 2; let (hash, _) = self.parse_embedded_triple_internal(input, pos)?;
228 self.stack.current().subject = Some(hash);
229 } else {
230 match self.parse_token(input, pos)? {
231 Some(hash) => self.stack.current().subject = Some(hash),
232 None => return Err(RdfStarParseError::MalformedEmbeddedTriple),
233 }
234 }
235
236 {
238 let frame = self.stack.current();
239 Self::expect_state(
240 frame,
241 ParsingState::ExpectPredicate,
242 ParsingState::ExpectObject,
243 )?;
244 }
245 match self.parse_token(input, pos)? {
246 Some(hash) => self.stack.current().predicate = Some(hash),
247 None => return Err(RdfStarParseError::MalformedEmbeddedTriple),
248 }
249
250 {
252 let frame = self.stack.current();
253 Self::expect_state(
254 frame,
255 ParsingState::ExpectObject,
256 ParsingState::ExpectEmbeddedEnd,
257 )?;
258 }
259 while *pos < input.len() && input[*pos].is_ascii_whitespace() {
260 *pos += 1;
261 }
262 if *pos + 1 < input.len() && input[*pos] == b'<' && input[*pos + 1] == b'<' {
263 *pos += 2; let (hash, _) = self.parse_embedded_triple_internal(input, pos)?;
265 self.stack.current().object = Some(hash);
266 } else {
267 match self.parse_token(input, pos)? {
268 Some(hash) => self.stack.current().object = Some(hash),
269 None => {
270 return Err(RdfStarParseError::MalformedEmbeddedTriple);
271 }
272 }
273 }
274
275 {
277 let frame = self.stack.current();
278 Self::expect_state(
279 frame,
280 ParsingState::ExpectEmbeddedEnd,
281 ParsingState::ExpectSubject,
282 )?;
283 }
284 while *pos + 1 < input.len() && input[*pos].is_ascii_whitespace() {
285 *pos += 1;
286 }
287 if *pos + 1 >= input.len() || input[*pos] != b'>' || input[*pos + 1] != b'>' {
288 return Err(RdfStarParseError::MalformedEmbeddedTriple);
289 }
290 *pos += 2;
291
292 if !self.stack.is_empty() && self.stack.depth() > 1 {
294 }
296 let frame = self
297 .stack
298 .pop()
299 .ok_or(RdfStarParseError::MalformedEmbeddedTriple)?;
300
301 let subject = frame
302 .subject
303 .ok_or(RdfStarParseError::MalformedEmbeddedTriple)?;
304 let predicate = frame
305 .predicate
306 .ok_or(RdfStarParseError::MalformedEmbeddedTriple)?;
307 let object = frame
308 .object
309 .ok_or(RdfStarParseError::MalformedEmbeddedTriple)?;
310
311 let virtual_id = generate_embedded_triple_id(subject, predicate, object);
313
314 Ok((virtual_id, [subject, predicate, object]))
315 }
316
317 fn parse_star_annotation(
327 &mut self,
328 input: &[u8],
329 ) -> Result<(u64, [u64; 3], Option<(u64, u64)>), RdfStarParseError> {
330 let mut pos = 0;
331 while pos < input.len() && input[pos].is_ascii_whitespace() {
332 pos += 1;
333 }
334 if pos + 1 < input.len() && input[pos] == b'<' && input[pos + 1] == b'<' {
335 pos += 2;
336 } else {
337 return Err(RdfStarParseError::MalformedEmbeddedTriple);
338 }
339 let (virtual_id, components) = self.parse_embedded_triple_internal(input, &mut pos)?;
340 let annotation = match self.parse_token(input, &mut pos)? {
342 Some(ann_predicate) => self
343 .parse_token(input, &mut pos)?
344 .map(|ann_object| (ann_predicate, ann_object)),
345 None => None,
346 };
347 Ok((virtual_id, components, annotation))
348 }
349}
350
351impl RdfStarParser for TurtleStarParser {
352 fn parse_embedded_triple(
353 &mut self,
354 input: &[u8],
355 ) -> Result<(u64, [u64; 3]), RdfStarParseError> {
356 let mut pos = 0;
357 while pos < input.len() && input[pos].is_ascii_whitespace() {
358 pos += 1;
359 }
360 if pos + 1 < input.len() && input[pos] == b'<' && input[pos + 1] == b'<' {
361 pos += 2;
362 }
363 self.parse_embedded_triple_internal(input, &mut pos)
364 }
365
366 fn parse_triple(&mut self, input: &[u8]) -> Result<(u64, u64, u64), RdfStarParseError> {
367 let mut pos = 0;
368
369 let skip_ws = |p: &mut usize| {
370 while *p < input.len() && input[*p].is_ascii_whitespace() {
371 *p += 1;
372 }
373 };
374
375 skip_ws(&mut pos);
376 let subject = if pos + 1 < input.len() && input[pos] == b'<' && input[pos + 1] == b'<' {
377 pos += 2; let (hash, _) = self.parse_embedded_triple_internal(input, &mut pos)?;
379 skip_ws(&mut pos);
382 if pos + 1 < input.len() && input[pos] == b'>' && input[pos + 1] == b'>' {
383 pos += 2;
384 }
385 hash
386 } else {
387 match self.parse_token(input, &mut pos)? {
388 Some(h) => h,
389 None => return Err(RdfStarParseError::InvalidSyntax),
390 }
391 };
392
393 skip_ws(&mut pos);
394 let predicate = match self.parse_token(input, &mut pos)? {
395 Some(h) => h,
396 None => return Err(RdfStarParseError::InvalidSyntax),
397 };
398
399 skip_ws(&mut pos);
400 let object = if pos + 1 < input.len() && input[pos] == b'<' && input[pos + 1] == b'<' {
401 pos += 2; let (hash, _) = self.parse_embedded_triple_internal(input, &mut pos)?;
403 skip_ws(&mut pos);
404 if pos + 1 < input.len() && input[pos] == b'>' && input[pos + 1] == b'>' {
405 pos += 2;
406 }
407 hash
408 } else {
409 match self.parse_token(input, &mut pos)? {
410 Some(h) => h,
411 None => return Err(RdfStarParseError::InvalidSyntax),
412 }
413 };
414
415 let _ = pos;
418 Ok((subject, predicate, object))
419 }
420
421 fn parse_quad(&mut self, _input: &[u8]) -> Result<(u64, u64, u64, u64), RdfStarParseError> {
422 Err(RdfStarParseError::UnsupportedFeature)
424 }
425
426 fn supports_quads(&self) -> bool {
427 false
428 }
429
430 fn supports_named_graphs(&self) -> bool {
431 false
432 }
433
434 fn format_name(&self) -> &'static str {
435 "Turtle-Star"
436 }
437}
438
439pub fn parse_turtle_star_into<R: Read, S: crate::sparql_library::quin_sink::QuinSink>(
444 reader: R,
445 context_hash: u64,
446 sink: &mut S,
447) -> Result<u64, Box<dyn std::error::Error>> {
448 let mut parser = TurtleStarParser::new(context_hash);
449 let mut count = 0;
450 let buf_reader = BufReader::new(reader);
451
452 for line in buf_reader.lines() {
453 let line = line?;
454 let l = line.trim();
455 if l.is_empty() || l.starts_with('#') || l.starts_with('@') {
456 continue;
457 }
458
459 let bytes = l.as_bytes();
461
462 if l.contains("<<") {
464 if let Ok((virtual_id, components, annotation)) = parser.parse_star_annotation(bytes) {
466 if let Some((ann_predicate, ann_object)) = annotation {
474 sink.push(NQuin {
475 subject: virtual_id,
476 predicate: ann_predicate,
477 object: ann_object,
478 context: context_hash,
479 metadata: 0b10 << 61,
480 parity: 0,
481 })?;
482 count += 1;
483 }
484
485 sink.push(NQuin {
488 subject: components[0],
489 predicate: components[1],
490 object: components[2],
491 context: context_hash, metadata: 0b10 << 61,
493 parity: 0,
494 })?;
495 count += 1;
496
497 }
503 } else {
504 if let Ok((subject, predicate, object)) = parser.parse_triple(bytes) {
506 sink.push(NQuin {
507 subject,
508 predicate,
509 object,
510 context: context_hash,
511 metadata: 0b10 << 61,
512 parity: 0,
513 })?;
514 count += 1;
515 }
516 }
517 }
518
519 Ok(count)
520}
521
522pub fn parse_turtle_star_stream<R: Read>(
523 reader: R,
524 context_hash: u64,
525 sorter: &mut crate::external_sort::ExternalSorter,
526) -> Result<u64, Box<dyn std::error::Error>> {
527 parse_turtle_star_into(reader, context_hash, sorter)
528}
529
530#[cfg(test)]
531mod tests {
532 use super::*;
533 use crate::rdf_star::RdfStarParser;
534
535 #[test]
536 fn test_turtle_star_parser_creation() {
537 let parser = TurtleStarParser::new(0);
538 assert_eq!(parser.format_name(), "Turtle-Star");
539 assert!(!parser.supports_quads());
540 assert!(!parser.supports_named_graphs());
541 assert_eq!(parser.stack.depth(), 0);
542 }
543
544 #[test]
545 fn test_parser_stack_push_pop() {
546 let mut stack = ParserStack::new();
547 assert!(stack.is_empty());
548
549 let frame = StackFrame::new();
550 stack.push(frame).unwrap();
551 assert_eq!(stack.depth(), 1);
552 assert!(!stack.is_empty());
553
554 let popped = stack.pop();
555 assert!(popped.is_some());
556 assert!(stack.is_empty());
557 }
558
559 #[test]
560 fn test_parser_stack_overflow() {
561 let mut stack = ParserStack::new();
562 let frame = StackFrame::new();
563
564 for _ in 0..MAX_NESTING_DEPTH {
566 stack.push(frame).unwrap();
567 }
568
569 assert!(stack.push(frame).is_err());
571 }
572
573 #[test]
574 fn test_parse_simple_triple() {
575 let mut parser = TurtleStarParser::new(0);
576 let input = b"Alice knows Bob";
577 let result = parser.parse_triple(input);
578 assert!(result.is_ok());
579 let (s, p, o) = result.unwrap();
580 assert_ne!(s, 0);
581 assert_ne!(p, 0);
582 assert_ne!(o, 0);
583 }
584
585 #[test]
586 fn test_parse_embedded_triple() {
587 let mut parser = TurtleStarParser::new(0);
588 let input = b"<< Alice knows Bob >>"; let result = parser.parse_embedded_triple(input);
590 assert!(result.is_ok());
591 let (virtual_id, components) = result.unwrap();
592 assert_ne!(virtual_id, 0);
593 assert_ne!(components[0], 0);
594 assert_ne!(components[1], 0);
595 assert_ne!(components[2], 0);
596
597 assert_eq!(parser.stack.depth(), 0);
599 }
600
601 struct VecSink(Vec<NQuin>);
602 impl crate::sparql_library::quin_sink::QuinSink for VecSink {
603 fn push(&mut self, q: NQuin) -> std::io::Result<()> {
604 self.0.push(q);
605 Ok(())
606 }
607 }
608
609 #[test]
610 fn parse_star_annotation_captures_outer_predicate_object() {
611 let mut parser = TurtleStarParser::new(0);
612 let (virtual_id, components, ann) = parser
613 .parse_star_annotation(b"<< Alice knows Bob >> certainty high")
614 .unwrap();
615 assert_eq!(
616 virtual_id,
617 generate_embedded_triple_id(components[0], components[1], components[2])
618 );
619 let (ann_p, ann_o) = ann.expect("outer annotation predicate/object");
620 assert_ne!(ann_p, 0);
621 assert_ne!(ann_o, 0);
622 assert_eq!(parser.stack.depth(), 0);
623 }
624
625 #[test]
626 fn parse_turtle_star_into_emits_linking_statement_and_inner_triple() {
627 let mut sink = VecSink(Vec::new());
628 let count =
629 parse_turtle_star_into(&b"<< Alice knows Bob >> certainty high\n"[..], 0, &mut sink)
630 .unwrap();
631 assert_eq!(count, 2, "expected outer linking statement + inner triple");
632 let inner = sink.0[1];
635 let vid = generate_embedded_triple_id(inner.subject, inner.predicate, inner.object);
636 assert_eq!(sink.0[0].subject, vid);
637 }
638
639 #[test]
640 fn parse_turtle_star_into_bare_embedded_triple_emits_only_inner() {
641 let mut sink = VecSink(Vec::new());
642 let count = parse_turtle_star_into(&b"<< Alice knows Bob >>\n"[..], 0, &mut sink).unwrap();
643 assert_eq!(count, 1, "bare embedded triple → just the inner triple");
644 }
645
646 #[test]
647 fn test_virtual_id_context_independence() {
648 use crate::lexicon::TAG_EMBEDDED;
649
650 let context1 = 12345u64;
652 let context2 = 67890u64;
653
654 let mut parser1 = TurtleStarParser::new(context1);
655 let mut parser2 = TurtleStarParser::new(context2);
656
657 let input = b"<< Alice knows Bob >>";
658 let (vid1, _) = parser1.parse_embedded_triple(input).unwrap();
659 let (vid2, _) = parser2.parse_embedded_triple(input).unwrap();
660
661 assert_eq!(vid1, vid2, "Virtual ID should be context-independent");
662 assert_ne!(vid1 & TAG_EMBEDDED, 0, "TAG_EMBEDDED bit should be set");
663 }
664}
665
666fn star_iri_term(val: u64) -> String {
684 let mut buf = Vec::new();
685 let _ = crate::query::resolver::write_iri_term(val, &mut buf);
686 String::from_utf8_lossy(&buf).into_owned()
687}
688
689fn star_object_term(val: u64) -> String {
691 let mut buf = Vec::new();
692 let _ = crate::query::resolver::write_object_term(val, &mut buf);
693 String::from_utf8_lossy(&buf).into_owned()
694}
695
696fn star_iri_bare(val: u64) -> String {
698 if let Some(bytes) = crate::resolver::resolve_hash(val) {
699 String::from_utf8_lossy(bytes).into_owned()
700 } else if (val & crate::resolver::MSB_FLAG) != 0 {
701 format!("did:q42:ptr/{:016x}", val & !crate::resolver::MSB_FLAG)
702 } else {
703 format!("quin:hash/{val:016x}")
704 }
705}
706
707fn star_json_escape(s: &str) -> String {
709 let mut out = String::with_capacity(s.len() + 2);
710 for c in s.chars() {
711 match c {
712 '"' => out.push_str("\\\""),
713 '\\' => out.push_str("\\\\"),
714 '\n' => out.push_str("\\n"),
715 '\r' => out.push_str("\\r"),
716 '\t' => out.push_str("\\t"),
717 c if (c as u32) < 0x20 => out.push_str(&format!("\\u{:04x}", c as u32)),
718 c => out.push(c),
719 }
720 }
721 out
722}
723
724fn star_object_jsonld(val: u64) -> String {
728 if crate::resolver::resolve_hash(val).is_some() || (val & crate::resolver::MSB_FLAG) != 0 {
729 return format!(
730 r#"{{ "@id": "{}" }}"#,
731 star_json_escape(&star_iri_bare(val))
732 );
733 }
734 if let Some(lit) = crate::resolver::classify_inline_literal(val) {
735 return format!(
736 r#"{{ "@value": "{}", "@type": "{}" }}"#,
737 star_json_escape(&lit.to_string()),
738 star_json_escape(lit.datatype_iri())
739 );
740 }
741 format!(
742 r#"{{ "@id": "{}" }}"#,
743 star_json_escape(&star_iri_bare(val))
744 )
745}
746
747pub struct TurtleStarSerializer;
748
749impl TurtleStarSerializer {
750 pub fn new() -> Self {
751 Self
752 }
753}
754
755impl crate::rdf_star::RdfStarSerializer for TurtleStarSerializer {
756 fn serialize_embedded_triple(
757 &self,
758 _virtual_id: u64,
759 components: &[u64; 3],
760 ) -> Result<Vec<u8>, crate::rdf_star::RdfStarSerializeError> {
761 let output = format!(
763 "<< {} {} {} >>",
764 star_iri_term(components[0]),
765 star_iri_term(components[1]),
766 star_object_term(components[2])
767 );
768 Ok(output.into_bytes())
769 }
770
771 fn serialize_triple(
772 &self,
773 subject: u64,
774 predicate: u64,
775 object: u64,
776 ) -> Result<Vec<u8>, crate::rdf_star::RdfStarSerializeError> {
777 let output = format!(
778 "{} {} {} .",
779 star_iri_term(subject),
780 star_iri_term(predicate),
781 star_object_term(object)
782 );
783 Ok(output.into_bytes())
784 }
785
786 fn serialize_quad(
787 &self,
788 _subject: u64,
789 _predicate: u64,
790 _object: u64,
791 _graph: u64,
792 ) -> Result<Vec<u8>, crate::rdf_star::RdfStarSerializeError> {
793 Err(crate::rdf_star::RdfStarSerializeError::UnsupportedFeature)
795 }
796
797 fn supports_quads(&self) -> bool {
798 false
799 }
800
801 fn format_name(&self) -> &'static str {
802 "Turtle-Star"
803 }
804}
805
806pub struct CborLdStarSerializer;
814
815impl CborLdStarSerializer {
816 pub fn new() -> Self {
817 Self
818 }
819}
820
821impl crate::rdf_star::RdfStarSerializer for CborLdStarSerializer {
822 fn serialize_embedded_triple(
823 &self,
824 _virtual_id: u64,
825 components: &[u64; 3],
826 ) -> Result<Vec<u8>, crate::rdf_star::RdfStarSerializeError> {
827 use ciborium::ser;
830
831 let mut buffer = Vec::new();
832 let tagged = ciborium::tag::Required::<[u64; 3], 103>(*components);
833 ser::into_writer(&tagged, &mut buffer)
834 .map_err(|_| crate::rdf_star::RdfStarSerializeError::BufferTooSmall)?;
835
836 Ok(buffer)
837 }
838
839 fn serialize_triple(
840 &self,
841 subject: u64,
842 predicate: u64,
843 object: u64,
844 ) -> Result<Vec<u8>, crate::rdf_star::RdfStarSerializeError> {
845 use ciborium::ser;
846 let mut buffer = Vec::new();
847 ser::into_writer(&[subject, predicate, object], &mut buffer)
848 .map_err(|_| crate::rdf_star::RdfStarSerializeError::BufferTooSmall)?;
849 Ok(buffer)
850 }
851
852 fn serialize_quad(
853 &self,
854 subject: u64,
855 predicate: u64,
856 object: u64,
857 graph: u64,
858 ) -> Result<Vec<u8>, crate::rdf_star::RdfStarSerializeError> {
859 use ciborium::ser;
860 let mut buffer = Vec::new();
861 ser::into_writer(&[subject, predicate, object, graph], &mut buffer)
862 .map_err(|_| crate::rdf_star::RdfStarSerializeError::BufferTooSmall)?;
863 Ok(buffer)
864 }
865
866 fn supports_quads(&self) -> bool {
867 true
868 }
869
870 fn format_name(&self) -> &'static str {
871 "CBOR-LD-Star"
872 }
873}
874
875#[cfg(test)]
876mod cbor_serializer_tests {
877 use super::*;
878 use crate::rdf_star::RdfStarSerializer;
879
880 #[test]
881 fn test_cbor_serializer_creation() {
882 let serializer = CborLdStarSerializer::new();
883 assert_eq!(serializer.format_name(), "CBOR-LD-Star");
884 assert!(serializer.supports_quads());
885 }
886
887 #[test]
888 fn test_serialize_embedded_triple() {
889 let serializer = CborLdStarSerializer::new();
890 let components = [1u64, 2, 3];
891 let result = serializer.serialize_embedded_triple(0, &components);
892 assert!(result.is_ok());
893 let bytes = result.unwrap();
894 assert_eq!(bytes[0], 0xd8);
896 assert_eq!(bytes[1], 0x67);
897 }
898
899 #[test]
900 fn test_serialize_triple() {
901 let serializer = CborLdStarSerializer::new();
902 let result = serializer.serialize_triple(1, 2, 3);
903 assert!(result.is_ok());
904 let bytes = result.unwrap();
905 assert_eq!(bytes[0], 0x83); }
908
909 #[test]
910 fn test_serialize_quad() {
911 let serializer = CborLdStarSerializer::new();
912 let result = serializer.serialize_quad(1, 2, 3, 4);
913 assert!(result.is_ok());
914 let bytes = result.unwrap();
915 assert_eq!(bytes[0], 0x84); }
918}
919
920pub struct NTriplesStarSerializer;
924
925impl NTriplesStarSerializer {
926 pub fn new() -> Self {
927 Self
928 }
929}
930
931impl crate::rdf_star::RdfStarSerializer for NTriplesStarSerializer {
932 fn serialize_embedded_triple(
933 &self,
934 _virtual_id: u64,
935 components: &[u64; 3],
936 ) -> Result<Vec<u8>, crate::rdf_star::RdfStarSerializeError> {
937 let output = format!(
940 "<< {} {} {} >>",
941 star_iri_term(components[0]),
942 star_iri_term(components[1]),
943 star_object_term(components[2])
944 );
945 Ok(output.into_bytes())
946 }
947
948 fn serialize_triple(
949 &self,
950 subject: u64,
951 predicate: u64,
952 object: u64,
953 ) -> Result<Vec<u8>, crate::rdf_star::RdfStarSerializeError> {
954 let output = format!(
956 "{} {} {} .",
957 star_iri_term(subject),
958 star_iri_term(predicate),
959 star_object_term(object)
960 );
961 Ok(output.into_bytes())
962 }
963
964 fn serialize_quad(
965 &self,
966 _subject: u64,
967 _predicate: u64,
968 _object: u64,
969 _graph: u64,
970 ) -> Result<Vec<u8>, crate::rdf_star::RdfStarSerializeError> {
971 Err(crate::rdf_star::RdfStarSerializeError::UnsupportedFeature)
973 }
974
975 fn supports_quads(&self) -> bool {
976 false
977 }
978
979 fn format_name(&self) -> &'static str {
980 "N-Triples-Star"
981 }
982}
983
984pub struct NQuadsStarSerializer;
988
989impl NQuadsStarSerializer {
990 pub fn new() -> Self {
991 Self
992 }
993}
994
995impl crate::rdf_star::RdfStarSerializer for NQuadsStarSerializer {
996 fn serialize_embedded_triple(
997 &self,
998 _virtual_id: u64,
999 components: &[u64; 3],
1000 ) -> Result<Vec<u8>, crate::rdf_star::RdfStarSerializeError> {
1001 let output = format!(
1003 "<< {} {} {} >>",
1004 star_iri_term(components[0]),
1005 star_iri_term(components[1]),
1006 star_object_term(components[2])
1007 );
1008 Ok(output.into_bytes())
1009 }
1010
1011 fn serialize_triple(
1012 &self,
1013 subject: u64,
1014 predicate: u64,
1015 object: u64,
1016 ) -> Result<Vec<u8>, crate::rdf_star::RdfStarSerializeError> {
1017 let output = format!(
1019 "{} {} {} .",
1020 star_iri_term(subject),
1021 star_iri_term(predicate),
1022 star_object_term(object)
1023 );
1024 Ok(output.into_bytes())
1025 }
1026
1027 fn serialize_quad(
1028 &self,
1029 subject: u64,
1030 predicate: u64,
1031 object: u64,
1032 graph: u64,
1033 ) -> Result<Vec<u8>, crate::rdf_star::RdfStarSerializeError> {
1034 let output = format!(
1036 "{} {} {} {} .",
1037 star_iri_term(subject),
1038 star_iri_term(predicate),
1039 star_object_term(object),
1040 star_iri_term(graph)
1041 );
1042 Ok(output.into_bytes())
1043 }
1044
1045 fn supports_quads(&self) -> bool {
1046 true
1047 }
1048
1049 fn format_name(&self) -> &'static str {
1050 "N-Quads-Star"
1051 }
1052}
1053
1054pub struct JsonLdStarSerializer;
1058
1059impl JsonLdStarSerializer {
1060 pub fn new() -> Self {
1061 Self
1062 }
1063}
1064
1065impl crate::rdf_star::RdfStarSerializer for JsonLdStarSerializer {
1066 fn serialize_embedded_triple(
1067 &self,
1068 _virtual_id: u64,
1069 components: &[u64; 3],
1070 ) -> Result<Vec<u8>, crate::rdf_star::RdfStarSerializeError> {
1071 let output = format!(
1083 "{{ \"@annotation\": {{ \"subject\": \"{}\", \"predicate\": \"{}\", \"object\": {} }} }}",
1084 star_json_escape(&star_iri_bare(components[0])),
1085 star_json_escape(&star_iri_bare(components[1])),
1086 star_object_jsonld(components[2])
1087 );
1088 Ok(output.into_bytes())
1089 }
1090
1091 fn serialize_triple(
1092 &self,
1093 subject: u64,
1094 predicate: u64,
1095 object: u64,
1096 ) -> Result<Vec<u8>, crate::rdf_star::RdfStarSerializeError> {
1097 let output = format!(
1099 "{{ \"@id\": \"{}\", \"{}\": [ {} ] }}",
1100 star_json_escape(&star_iri_bare(subject)),
1101 star_json_escape(&star_iri_bare(predicate)),
1102 star_object_jsonld(object)
1103 );
1104 Ok(output.into_bytes())
1105 }
1106
1107 fn serialize_quad(
1108 &self,
1109 subject: u64,
1110 predicate: u64,
1111 object: u64,
1112 graph: u64,
1113 ) -> Result<Vec<u8>, crate::rdf_star::RdfStarSerializeError> {
1114 let output = format!(
1116 "{{ \"@id\": \"{}\", \"{}\": [ {} ], \"@graph\": \"{}\" }}",
1117 star_json_escape(&star_iri_bare(subject)),
1118 star_json_escape(&star_iri_bare(predicate)),
1119 star_object_jsonld(object),
1120 star_json_escape(&star_iri_bare(graph))
1121 );
1122 Ok(output.into_bytes())
1123 }
1124
1125 fn supports_quads(&self) -> bool {
1126 true
1127 }
1128
1129 fn format_name(&self) -> &'static str {
1130 "JSON-LD-Star"
1131 }
1132}
1133
1134#[cfg(test)]
1135mod additional_serializer_tests {
1136 use super::*;
1137 use crate::rdf_star::RdfStarSerializer;
1138
1139 #[test]
1140 fn test_ntriples_serializer() {
1141 let serializer = NTriplesStarSerializer::new();
1142 assert_eq!(serializer.format_name(), "N-Triples-Star");
1143 assert!(!serializer.supports_quads());
1144
1145 let components = [1u64, 2, 3];
1146 let result = serializer.serialize_embedded_triple(0, &components);
1147 assert!(result.is_ok());
1148 let output = String::from_utf8(result.unwrap()).unwrap();
1149 assert!(output.starts_with("<< "), "got: {output}");
1151 assert!(output.contains("quin:hash/"), "resolved terms: {output}");
1153 }
1154
1155 #[test]
1156 fn test_nquads_serializer() {
1157 let serializer = NQuadsStarSerializer::new();
1158 assert_eq!(serializer.format_name(), "N-Quads-Star");
1159 assert!(serializer.supports_quads());
1160
1161 let result = serializer.serialize_quad(1, 2, 3, 4);
1162 assert!(result.is_ok());
1163 let output = String::from_utf8(result.unwrap()).unwrap();
1164 assert!(
1166 output.contains("quin:hash/0000000000000004"),
1167 "graph term must be resolved: {output}"
1168 );
1169 }
1170
1171 #[test]
1172 fn test_jsonld_serializer() {
1173 let serializer = JsonLdStarSerializer::new();
1174 assert_eq!(serializer.format_name(), "JSON-LD-Star");
1175 assert!(serializer.supports_quads());
1176
1177 let components = [1u64, 2, 3];
1178 let result = serializer.serialize_embedded_triple(0, &components);
1179 assert!(result.is_ok());
1180 let output = String::from_utf8(result.unwrap()).unwrap();
1181 assert!(output.contains("@annotation"));
1182 }
1183}
1184
1185pub struct TrigStarSerializer {
1189 current_graph: u64,
1190}
1191
1192impl TrigStarSerializer {
1193 pub fn new() -> Self {
1194 Self { current_graph: 0 }
1195 }
1196
1197 pub fn set_current_graph(&mut self, graph_hash: u64) {
1198 self.current_graph = graph_hash;
1199 }
1200}
1201
1202impl crate::rdf_star::RdfStarSerializer for TrigStarSerializer {
1203 fn serialize_embedded_triple(
1204 &self,
1205 _virtual_id: u64,
1206 components: &[u64; 3],
1207 ) -> Result<Vec<u8>, crate::rdf_star::RdfStarSerializeError> {
1208 let output = format!(
1210 "<< {} {} {} >>",
1211 star_iri_term(components[0]),
1212 star_iri_term(components[1]),
1213 star_object_term(components[2])
1214 );
1215 Ok(output.into_bytes())
1216 }
1217
1218 fn serialize_triple(
1219 &self,
1220 subject: u64,
1221 predicate: u64,
1222 object: u64,
1223 ) -> Result<Vec<u8>, crate::rdf_star::RdfStarSerializeError> {
1224 let output = format!(
1225 "{} {} {} .",
1226 star_iri_term(subject),
1227 star_iri_term(predicate),
1228 star_object_term(object)
1229 );
1230 Ok(output.into_bytes())
1231 }
1232
1233 fn serialize_quad(
1234 &self,
1235 subject: u64,
1236 predicate: u64,
1237 object: u64,
1238 graph: u64,
1239 ) -> Result<Vec<u8>, crate::rdf_star::RdfStarSerializeError> {
1240 let triple = format!(
1241 "{} {} {} .",
1242 star_iri_term(subject),
1243 star_iri_term(predicate),
1244 star_object_term(object)
1245 );
1246 let output = if graph == 0 {
1248 triple
1249 } else {
1250 format!("GRAPH {} {{ {} }}", star_iri_term(graph), triple)
1251 };
1252 Ok(output.into_bytes())
1253 }
1254
1255 fn supports_quads(&self) -> bool {
1256 true
1257 }
1258
1259 fn format_name(&self) -> &'static str {
1260 "Trig-Star"
1261 }
1262}
1263
1264#[cfg(test)]
1265mod trig_serializer_tests {
1266 use super::*;
1267 use crate::rdf_star::RdfStarSerializer;
1268
1269 #[test]
1270 fn test_trig_serializer() {
1271 let serializer = TrigStarSerializer::new();
1272 assert_eq!(serializer.format_name(), "Trig-Star");
1273 assert!(serializer.supports_quads());
1274
1275 let result = serializer.serialize_quad(1, 2, 3, 0);
1276 assert!(result.is_ok());
1277 }
1278}
1279
1280pub struct N3StarSerializer {
1284 variables: std::collections::HashMap<u64, String>,
1286}
1287
1288impl N3StarSerializer {
1289 pub fn new() -> Self {
1290 Self {
1291 variables: std::collections::HashMap::new(),
1292 }
1293 }
1294
1295 pub fn bind_variable(&mut self, hash: u64, name: String) {
1296 self.variables.insert(hash, name);
1297 }
1298}
1299
1300impl crate::rdf_star::RdfStarSerializer for N3StarSerializer {
1301 fn serialize_embedded_triple(
1302 &self,
1303 _virtual_id: u64,
1304 components: &[u64; 3],
1305 ) -> Result<Vec<u8>, crate::rdf_star::RdfStarSerializeError> {
1306 let output = format!(
1308 "<< {} {} {} >>",
1309 star_iri_term(components[0]),
1310 star_iri_term(components[1]),
1311 star_object_term(components[2])
1312 );
1313 Ok(output.into_bytes())
1314 }
1315
1316 fn serialize_triple(
1317 &self,
1318 subject: u64,
1319 predicate: u64,
1320 object: u64,
1321 ) -> Result<Vec<u8>, crate::rdf_star::RdfStarSerializeError> {
1322 let s = self
1326 .variables
1327 .get(&subject)
1328 .cloned()
1329 .unwrap_or_else(|| star_iri_term(subject));
1330 let p = self
1331 .variables
1332 .get(&predicate)
1333 .cloned()
1334 .unwrap_or_else(|| star_iri_term(predicate));
1335 let o = self
1336 .variables
1337 .get(&object)
1338 .cloned()
1339 .unwrap_or_else(|| star_object_term(object));
1340
1341 let output = format!("{} {} {} .", s, p, o);
1342 Ok(output.into_bytes())
1343 }
1344
1345 fn serialize_quad(
1346 &self,
1347 subject: u64,
1348 predicate: u64,
1349 object: u64,
1350 _graph: u64,
1351 ) -> Result<Vec<u8>, crate::rdf_star::RdfStarSerializeError> {
1352 self.serialize_triple(subject, predicate, object)
1354 }
1355
1356 fn supports_quads(&self) -> bool {
1357 false
1358 }
1359
1360 fn format_name(&self) -> &'static str {
1361 "N3-Star"
1362 }
1363}
1364
1365#[cfg(test)]
1366mod n3_serializer_tests {
1367 use super::*;
1368 use crate::rdf_star::RdfStarSerializer;
1369
1370 #[test]
1371 fn test_n3_serializer() {
1372 let serializer = N3StarSerializer::new();
1373 assert_eq!(serializer.format_name(), "N3-Star");
1374 assert!(!serializer.supports_quads());
1375
1376 let result = serializer.serialize_triple(1, 2, 3);
1377 assert!(result.is_ok());
1378 }
1379
1380 #[test]
1381 fn test_n3_serializer_variables() {
1382 let mut serializer = N3StarSerializer::new();
1383 serializer.bind_variable(1, "x".to_string());
1384 serializer.bind_variable(2, "knows".to_string());
1385 serializer.bind_variable(3, "y".to_string());
1386
1387 let result = serializer.serialize_triple(1, 2, 3);
1388 assert!(result.is_ok());
1389 let output = String::from_utf8(result.unwrap()).unwrap();
1390 assert!(output.contains("x"));
1391 }
1392}