1use crate::model::{BlankNode, Literal, NamedNode, Object, Predicate, RdfTerm, Subject, Triple};
30use crate::OxirsError;
31use serde::{Deserialize, Serialize};
32use std::collections::hash_map::DefaultHasher;
33use std::fmt;
34use std::hash::{Hash, Hasher};
35
36#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
57pub struct TripleTerm {
58 subject: Subject,
59 predicate: Predicate,
60 object: Object,
61}
62
63impl TripleTerm {
64 pub fn new(
66 subject: impl Into<Subject>,
67 predicate: impl Into<Predicate>,
68 object: impl Into<Object>,
69 ) -> Self {
70 Self {
71 subject: subject.into(),
72 predicate: predicate.into(),
73 object: object.into(),
74 }
75 }
76
77 pub fn from_triple(triple: &Triple) -> Self {
79 Self {
80 subject: triple.subject().clone(),
81 predicate: triple.predicate().clone(),
82 object: triple.object().clone(),
83 }
84 }
85
86 pub fn from_owned_triple(triple: Triple) -> Self {
88 let (subject, predicate, object) = triple.into_parts();
89 Self {
90 subject,
91 predicate,
92 object,
93 }
94 }
95
96 pub fn subject(&self) -> &Subject {
98 &self.subject
99 }
100
101 pub fn predicate(&self) -> &Predicate {
103 &self.predicate
104 }
105
106 pub fn object(&self) -> &Object {
108 &self.object
109 }
110
111 pub fn into_parts(self) -> (Subject, Predicate, Object) {
113 (self.subject, self.predicate, self.object)
114 }
115
116 pub fn into_triple(self) -> Triple {
118 Triple::new(self.subject, self.predicate, self.object)
119 }
120
121 pub fn to_triple(&self) -> Triple {
123 Triple::new(
124 self.subject.clone(),
125 self.predicate.clone(),
126 self.object.clone(),
127 )
128 }
129
130 pub fn to_ntriples_string(&self) -> String {
134 format!("<< {} {} {} >>", self.subject, self.predicate, self.object)
135 }
136
137 pub fn parse(input: &str) -> Result<Self, OxirsError> {
145 let trimmed = input.trim();
146
147 if !trimmed.starts_with("<<") || !trimmed.ends_with(">>") {
148 return Err(OxirsError::Parse(
149 "Triple term must be enclosed in << >>".to_string(),
150 ));
151 }
152
153 let inner = trimmed[2..trimmed.len() - 2].trim();
155
156 if inner.is_empty() {
157 return Err(OxirsError::Parse("Triple term cannot be empty".to_string()));
158 }
159
160 let tokens = tokenize_triple_term(inner)?;
162
163 if tokens.len() != 3 {
164 return Err(OxirsError::Parse(format!(
165 "Triple term requires exactly 3 components (subject, predicate, object), got {}",
166 tokens.len()
167 )));
168 }
169
170 let subject = parse_subject(&tokens[0])?;
171 let predicate = parse_predicate(&tokens[1])?;
172 let object = parse_object(&tokens[2])?;
173
174 Ok(Self {
175 subject,
176 predicate,
177 object,
178 })
179 }
180
181 pub fn has_iri_subject(&self) -> bool {
183 matches!(self.subject, Subject::NamedNode(_))
184 }
185
186 pub fn has_blank_subject(&self) -> bool {
188 matches!(self.subject, Subject::BlankNode(_))
189 }
190
191 pub fn has_literal_object(&self) -> bool {
193 matches!(self.object, Object::Literal(_))
194 }
195
196 pub fn has_iri_object(&self) -> bool {
198 matches!(self.object, Object::NamedNode(_))
199 }
200
201 pub fn has_variables(&self) -> bool {
203 matches!(self.subject, Subject::Variable(_))
204 || matches!(self.predicate, Predicate::Variable(_))
205 || matches!(self.object, Object::Variable(_))
206 }
207
208 pub fn deterministic_hash(&self) -> u64 {
210 let mut hasher = DefaultHasher::new();
211 self.hash(&mut hasher);
212 hasher.finish()
213 }
214
215 pub fn structurally_equal(&self, other: &TripleTerm) -> bool {
218 self.subject == other.subject
219 && self.predicate == other.predicate
220 && self.object == other.object
221 }
222
223 pub fn with_subject(mut self, subject: impl Into<Subject>) -> Self {
225 self.subject = subject.into();
226 self
227 }
228
229 pub fn with_predicate(mut self, predicate: impl Into<Predicate>) -> Self {
231 self.predicate = predicate.into();
232 self
233 }
234
235 pub fn with_object(mut self, object: impl Into<Object>) -> Self {
237 self.object = object.into();
238 self
239 }
240
241 pub fn is_ground(&self) -> bool {
248 !self.has_variables()
249 }
250
251 pub fn iri_count(&self) -> usize {
253 let mut count = 0;
254 if matches!(self.subject, Subject::NamedNode(_)) {
255 count += 1;
256 }
257 if matches!(self.predicate, Predicate::NamedNode(_)) {
258 count += 1;
259 }
260 if matches!(self.object, Object::NamedNode(_)) {
261 count += 1;
262 }
263 count
264 }
265}
266
267impl fmt::Display for TripleTerm {
268 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
269 write!(
270 f,
271 "<< {} {} {} >>",
272 self.subject, self.predicate, self.object
273 )
274 }
275}
276
277impl RdfTerm for TripleTerm {
278 fn as_str(&self) -> &str {
279 "<<triple-term>>"
280 }
281
282 fn is_quoted_triple(&self) -> bool {
283 true
284 }
285}
286
287impl From<Triple> for TripleTerm {
288 fn from(triple: Triple) -> Self {
289 Self::from_owned_triple(triple)
290 }
291}
292
293impl From<TripleTerm> for Triple {
294 fn from(tt: TripleTerm) -> Self {
295 tt.into_triple()
296 }
297}
298
299impl From<&Triple> for TripleTerm {
300 fn from(triple: &Triple) -> Self {
301 Self::from_triple(triple)
302 }
303}
304
305#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
307pub struct TripleTermRef<'a> {
308 subject: &'a Subject,
309 predicate: &'a Predicate,
310 object: &'a Object,
311}
312
313impl<'a> TripleTermRef<'a> {
314 pub fn new(subject: &'a Subject, predicate: &'a Predicate, object: &'a Object) -> Self {
316 Self {
317 subject,
318 predicate,
319 object,
320 }
321 }
322
323 pub fn from_triple_term(tt: &'a TripleTerm) -> Self {
325 Self {
326 subject: &tt.subject,
327 predicate: &tt.predicate,
328 object: &tt.object,
329 }
330 }
331
332 pub fn subject(&self) -> &'a Subject {
334 self.subject
335 }
336
337 pub fn predicate(&self) -> &'a Predicate {
339 self.predicate
340 }
341
342 pub fn object(&self) -> &'a Object {
344 self.object
345 }
346
347 pub fn to_owned(&self) -> TripleTerm {
349 TripleTerm {
350 subject: self.subject.clone(),
351 predicate: self.predicate.clone(),
352 object: self.object.clone(),
353 }
354 }
355}
356
357impl fmt::Display for TripleTermRef<'_> {
358 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
359 write!(
360 f,
361 "<< {} {} {} >>",
362 self.subject, self.predicate, self.object
363 )
364 }
365}
366
367#[derive(Debug, Clone, Default)]
369pub struct TripleTermSet {
370 terms: Vec<TripleTerm>,
371}
372
373impl TripleTermSet {
374 pub fn new() -> Self {
376 Self { terms: Vec::new() }
377 }
378
379 pub fn with_capacity(capacity: usize) -> Self {
381 Self {
382 terms: Vec::with_capacity(capacity),
383 }
384 }
385
386 pub fn insert(&mut self, term: TripleTerm) -> bool {
388 if self.terms.contains(&term) {
389 false
390 } else {
391 self.terms.push(term);
392 true
393 }
394 }
395
396 pub fn len(&self) -> usize {
398 self.terms.len()
399 }
400
401 pub fn is_empty(&self) -> bool {
403 self.terms.is_empty()
404 }
405
406 pub fn contains(&self, term: &TripleTerm) -> bool {
408 self.terms.contains(term)
409 }
410
411 pub fn iter(&self) -> impl Iterator<Item = &TripleTerm> {
413 self.terms.iter()
414 }
415
416 pub fn filter_by_predicate(&self, predicate_iri: &str) -> Vec<&TripleTerm> {
418 self.terms
419 .iter()
420 .filter(|tt| match &tt.predicate {
421 Predicate::NamedNode(nn) => nn.as_str() == predicate_iri,
422 Predicate::Variable(_) => false,
423 })
424 .collect()
425 }
426
427 pub fn filter_by_subject_iri(&self, subject_iri: &str) -> Vec<&TripleTerm> {
429 self.terms
430 .iter()
431 .filter(|tt| match &tt.subject {
432 Subject::NamedNode(nn) => nn.as_str() == subject_iri,
433 _ => false,
434 })
435 .collect()
436 }
437
438 pub fn ground_terms(&self) -> Vec<&TripleTerm> {
440 self.terms.iter().filter(|tt| tt.is_ground()).collect()
441 }
442
443 pub fn remove(&mut self, term: &TripleTerm) -> bool {
445 if let Some(pos) = self.terms.iter().position(|t| t == term) {
446 self.terms.swap_remove(pos);
447 true
448 } else {
449 false
450 }
451 }
452
453 pub fn drain(&mut self) -> impl Iterator<Item = TripleTerm> + '_ {
455 self.terms.drain(..)
456 }
457
458 pub fn to_triples(&self) -> Vec<Triple> {
460 self.terms.iter().map(|tt| tt.to_triple()).collect()
461 }
462}
463
464impl IntoIterator for TripleTermSet {
465 type Item = TripleTerm;
466 type IntoIter = std::vec::IntoIter<TripleTerm>;
467
468 fn into_iter(self) -> Self::IntoIter {
469 self.terms.into_iter()
470 }
471}
472
473impl FromIterator<TripleTerm> for TripleTermSet {
474 fn from_iter<I: IntoIterator<Item = TripleTerm>>(iter: I) -> Self {
475 let mut set = Self::new();
476 for term in iter {
477 set.insert(term);
478 }
479 set
480 }
481}
482
483fn tokenize_triple_term(input: &str) -> Result<Vec<String>, OxirsError> {
487 let mut tokens = Vec::new();
488 let chars: Vec<char> = input.chars().collect();
489 let mut i = 0;
490 let len = chars.len();
491
492 while i < len {
493 while i < len && chars[i].is_whitespace() {
495 i += 1;
496 }
497 if i >= len {
498 break;
499 }
500
501 if chars[i] == '<' {
502 let start = i;
504 i += 1;
505 while i < len && chars[i] != '>' {
506 i += 1;
507 }
508 if i >= len {
509 return Err(OxirsError::Parse(
510 "Unterminated IRI in triple term".to_string(),
511 ));
512 }
513 i += 1; let token: String = chars[start..i].iter().collect();
515 tokens.push(token);
516 } else if chars[i] == '"' {
517 let start = i;
519 i += 1;
520 while i < len && chars[i] != '"' {
521 if chars[i] == '\\' {
522 i += 1; }
524 i += 1;
525 }
526 if i >= len {
527 return Err(OxirsError::Parse(
528 "Unterminated literal in triple term".to_string(),
529 ));
530 }
531 i += 1; if i < len && chars[i] == '@' {
535 i += 1;
537 while i < len && (chars[i].is_alphanumeric() || chars[i] == '-') {
538 i += 1;
539 }
540 } else if i + 1 < len && chars[i] == '^' && chars[i + 1] == '^' {
541 i += 2;
543 if i < len && chars[i] == '<' {
544 while i < len && chars[i] != '>' {
545 i += 1;
546 }
547 if i < len {
548 i += 1; }
550 }
551 }
552
553 let token: String = chars[start..i].iter().collect();
554 tokens.push(token);
555 } else if chars[i] == '_' && i + 1 < len && chars[i + 1] == ':' {
556 let start = i;
558 i += 2;
559 while i < len && !chars[i].is_whitespace() {
560 i += 1;
561 }
562 let token: String = chars[start..i].iter().collect();
563 tokens.push(token);
564 } else if chars[i] == '?' || chars[i] == '$' {
565 let start = i;
567 i += 1;
568 while i < len && (chars[i].is_alphanumeric() || chars[i] == '_') {
569 i += 1;
570 }
571 let token: String = chars[start..i].iter().collect();
572 tokens.push(token);
573 } else {
574 let start = i;
576 while i < len && !chars[i].is_whitespace() {
577 i += 1;
578 }
579 let token: String = chars[start..i].iter().collect();
580 tokens.push(token);
581 }
582 }
583
584 Ok(tokens)
585}
586
587fn parse_subject(token: &str) -> Result<Subject, OxirsError> {
589 if token.starts_with('<') && token.ends_with('>') {
590 let iri = &token[1..token.len() - 1];
591 Ok(Subject::NamedNode(NamedNode::new(iri)?))
592 } else if let Some(stripped) = token.strip_prefix("_:") {
593 Ok(Subject::BlankNode(BlankNode::new(stripped)?))
594 } else if token.starts_with('?') || token.starts_with('$') {
595 let var_name = &token[1..];
596 Ok(Subject::Variable(crate::model::Variable::new(var_name)?))
597 } else {
598 Err(OxirsError::Parse(format!(
599 "Invalid subject in triple term: '{token}'"
600 )))
601 }
602}
603
604fn parse_predicate(token: &str) -> Result<Predicate, OxirsError> {
606 if token.starts_with('<') && token.ends_with('>') {
607 let iri = &token[1..token.len() - 1];
608 Ok(Predicate::NamedNode(NamedNode::new(iri)?))
609 } else if token.starts_with('?') || token.starts_with('$') {
610 let var_name = &token[1..];
611 Ok(Predicate::Variable(crate::model::Variable::new(var_name)?))
612 } else {
613 Err(OxirsError::Parse(format!(
614 "Invalid predicate in triple term: '{token}'. Predicates must be IRIs or variables."
615 )))
616 }
617}
618
619fn parse_object(token: &str) -> Result<Object, OxirsError> {
621 if token.starts_with('<') && token.ends_with('>') {
622 let iri = &token[1..token.len() - 1];
623 Ok(Object::NamedNode(NamedNode::new(iri)?))
624 } else if let Some(stripped) = token.strip_prefix("_:") {
625 Ok(Object::BlankNode(BlankNode::new(stripped)?))
626 } else if token.starts_with('"') {
627 let literal = parse_literal_token(token)?;
629 Ok(Object::Literal(literal))
630 } else if token.starts_with('?') || token.starts_with('$') {
631 let var_name = &token[1..];
632 Ok(Object::Variable(crate::model::Variable::new(var_name)?))
633 } else {
634 Err(OxirsError::Parse(format!(
635 "Invalid object in triple term: '{token}'"
636 )))
637 }
638}
639
640fn parse_literal_token(token: &str) -> Result<Literal, OxirsError> {
642 if !token.starts_with('"') {
643 return Err(OxirsError::Parse(format!("Invalid literal: '{token}'")));
644 }
645
646 let bytes = token.as_bytes();
648 let mut end_quote = 1;
649 while end_quote < bytes.len() {
650 if bytes[end_quote] == b'"' && (end_quote == 1 || bytes[end_quote - 1] != b'\\') {
651 break;
652 }
653 end_quote += 1;
654 }
655
656 if end_quote >= bytes.len() {
657 return Err(OxirsError::Parse(format!(
658 "Unterminated literal: '{token}'"
659 )));
660 }
661
662 let value = &token[1..end_quote];
663 let rest = &token[end_quote + 1..];
664
665 if let Some(lang) = rest.strip_prefix('@') {
666 Literal::new_lang(value, lang)
668 .map_err(|e| OxirsError::Parse(format!("Invalid language tag '{lang}': {e}")))
669 } else if let Some(dt_part) = rest.strip_prefix("^^") {
670 if dt_part.starts_with('<') && dt_part.ends_with('>') {
672 let dt_iri = &dt_part[1..dt_part.len() - 1];
673 let datatype = NamedNode::new(dt_iri)?;
674 Ok(Literal::new_typed(value, datatype))
675 } else {
676 Err(OxirsError::Parse(format!(
677 "Invalid datatype IRI in literal: '{dt_part}'"
678 )))
679 }
680 } else {
681 Ok(Literal::new(value))
683 }
684}
685
686#[cfg(test)]
689mod tests {
690 use super::*;
691 use crate::model::{BlankNode, Literal, NamedNode, Variable};
692
693 fn example_iri(local: &str) -> NamedNode {
694 NamedNode::new(format!("http://example.org/{local}")).expect("valid IRI")
695 }
696
697 fn make_tt(s: &str, p: &str, o_iri: &str) -> TripleTerm {
698 TripleTerm::new(example_iri(s), example_iri(p), example_iri(o_iri))
699 }
700
701 #[test]
704 fn test_new_with_named_nodes() {
705 let tt = make_tt("alice", "knows", "bob");
706 assert!(tt.has_iri_subject());
707 assert!(tt.has_iri_object());
708 assert!(!tt.has_literal_object());
709 assert!(!tt.has_blank_subject());
710 assert!(!tt.has_variables());
711 assert!(tt.is_ground());
712 }
713
714 #[test]
715 fn test_new_with_literal_object() {
716 let tt = TripleTerm::new(
717 example_iri("alice"),
718 example_iri("name"),
719 Literal::new("Alice"),
720 );
721 assert!(tt.has_literal_object());
722 assert!(!tt.has_iri_object());
723 assert!(tt.is_ground());
724 }
725
726 #[test]
727 fn test_new_with_blank_node_subject() {
728 let bn = BlankNode::new("b1").expect("valid blank node");
729 let tt = TripleTerm::new(bn, example_iri("type"), example_iri("Person"));
730 assert!(tt.has_blank_subject());
731 assert!(!tt.has_iri_subject());
732 assert!(tt.is_ground());
733 }
734
735 #[test]
736 fn test_new_with_variable() {
737 let var = Variable::new("x").expect("valid var");
738 let tt = TripleTerm::new(var, example_iri("knows"), example_iri("bob"));
739 assert!(tt.has_variables());
740 assert!(!tt.is_ground());
741 }
742
743 #[test]
744 fn test_from_triple() {
745 let triple = Triple::new(
746 example_iri("alice"),
747 example_iri("knows"),
748 example_iri("bob"),
749 );
750 let tt = TripleTerm::from_triple(&triple);
751 assert_eq!(tt.subject(), triple.subject());
752 assert_eq!(tt.predicate(), triple.predicate());
753 assert_eq!(tt.object(), triple.object());
754 }
755
756 #[test]
757 fn test_from_owned_triple() {
758 let triple = Triple::new(
759 example_iri("alice"),
760 example_iri("knows"),
761 example_iri("bob"),
762 );
763 let triple_clone = triple.clone();
764 let tt = TripleTerm::from_owned_triple(triple);
765 assert_eq!(tt.subject(), triple_clone.subject());
766 }
767
768 #[test]
771 fn test_into_triple() {
772 let tt = make_tt("alice", "knows", "bob");
773 let triple = tt.into_triple();
774 assert_eq!(triple.subject(), &Subject::NamedNode(example_iri("alice")));
775 }
776
777 #[test]
778 fn test_from_triple_trait() {
779 let triple = Triple::new(
780 example_iri("alice"),
781 example_iri("knows"),
782 example_iri("bob"),
783 );
784 let tt: TripleTerm = triple.clone().into();
785 assert_eq!(tt.to_triple(), triple);
786 }
787
788 #[test]
789 fn test_from_ref_triple() {
790 let triple = Triple::new(
791 example_iri("alice"),
792 example_iri("knows"),
793 example_iri("bob"),
794 );
795 let tt: TripleTerm = (&triple).into();
796 assert_eq!(tt.to_triple(), triple);
797 }
798
799 #[test]
800 fn test_to_triple_roundtrip() {
801 let tt = make_tt("s", "p", "o");
802 let triple = tt.to_triple();
803 let tt2 = TripleTerm::from_triple(&triple);
804 assert_eq!(tt2, make_tt("s", "p", "o"));
805 }
806
807 #[test]
810 fn test_display() {
811 let tt = make_tt("alice", "knows", "bob");
812 let display = tt.to_string();
813 assert!(display.starts_with("<<"));
814 assert!(display.ends_with(">>"));
815 assert!(display.contains("http://example.org/alice"));
816 assert!(display.contains("http://example.org/knows"));
817 assert!(display.contains("http://example.org/bob"));
818 }
819
820 #[test]
821 fn test_ntriples_string() {
822 let tt = make_tt("alice", "knows", "bob");
823 let s = tt.to_ntriples_string();
824 assert_eq!(
825 s,
826 "<< <http://example.org/alice> <http://example.org/knows> <http://example.org/bob> >>"
827 );
828 }
829
830 #[test]
831 fn test_parse_basic_iris() {
832 let input =
833 "<< <http://example.org/alice> <http://example.org/knows> <http://example.org/bob> >>";
834 let tt = TripleTerm::parse(input).expect("should parse");
835 assert_eq!(tt, make_tt("alice", "knows", "bob"));
836 }
837
838 #[test]
839 fn test_parse_with_literal() {
840 let input = r#"<< <http://example.org/alice> <http://example.org/name> "Alice" >>"#;
841 let tt = TripleTerm::parse(input).expect("should parse");
842 assert!(tt.has_literal_object());
843 }
844
845 #[test]
846 fn test_parse_with_lang_literal() {
847 let input = r#"<< <http://example.org/alice> <http://example.org/name> "Alice"@en >>"#;
848 let tt = TripleTerm::parse(input).expect("should parse");
849 if let Object::Literal(lit) = tt.object() {
850 assert_eq!(lit.language(), Some("en"));
851 } else {
852 panic!("Expected literal object");
853 }
854 }
855
856 #[test]
857 fn test_parse_with_typed_literal() {
858 let input = r#"<< <http://example.org/alice> <http://example.org/age> "30"^^<http://www.w3.org/2001/XMLSchema#integer> >>"#;
859 let tt = TripleTerm::parse(input).expect("should parse");
860 if let Object::Literal(lit) = tt.object() {
861 assert_eq!(lit.value(), "30");
862 } else {
863 panic!("Expected literal object");
864 }
865 }
866
867 #[test]
868 fn test_parse_with_blank_node() {
869 let input = "<< _:b1 <http://example.org/type> <http://example.org/Person> >>";
870 let tt = TripleTerm::parse(input).expect("should parse");
871 assert!(tt.has_blank_subject());
872 }
873
874 #[test]
875 fn test_parse_with_variable() {
876 let input = "<< ?s <http://example.org/knows> ?o >>";
877 let tt = TripleTerm::parse(input).expect("should parse");
878 assert!(tt.has_variables());
879 }
880
881 #[test]
882 fn test_parse_error_no_delimiters() {
883 let result = TripleTerm::parse("not a triple term");
884 assert!(result.is_err());
885 }
886
887 #[test]
888 fn test_parse_error_empty() {
889 let result = TripleTerm::parse("<< >>");
890 assert!(result.is_err());
891 }
892
893 #[test]
894 fn test_parse_error_too_few_components() {
895 let result = TripleTerm::parse("<< <http://example.org/a> <http://example.org/b> >>");
896 assert!(result.is_err());
897 }
898
899 #[test]
900 fn test_parse_roundtrip() {
901 let tt = make_tt("alice", "knows", "bob");
902 let s = tt.to_ntriples_string();
903 let parsed = TripleTerm::parse(&s).expect("should parse");
904 assert_eq!(parsed, tt);
905 }
906
907 #[test]
910 fn test_equality_same_components() {
911 let tt1 = make_tt("alice", "knows", "bob");
912 let tt2 = make_tt("alice", "knows", "bob");
913 assert_eq!(tt1, tt2);
914 }
915
916 #[test]
917 fn test_inequality_different_subject() {
918 let tt1 = make_tt("alice", "knows", "bob");
919 let tt2 = make_tt("carol", "knows", "bob");
920 assert_ne!(tt1, tt2);
921 }
922
923 #[test]
924 fn test_inequality_different_predicate() {
925 let tt1 = make_tt("alice", "knows", "bob");
926 let tt2 = make_tt("alice", "likes", "bob");
927 assert_ne!(tt1, tt2);
928 }
929
930 #[test]
931 fn test_inequality_different_object() {
932 let tt1 = make_tt("alice", "knows", "bob");
933 let tt2 = make_tt("alice", "knows", "carol");
934 assert_ne!(tt1, tt2);
935 }
936
937 #[test]
938 fn test_hash_consistency() {
939 let tt1 = make_tt("alice", "knows", "bob");
940 let tt2 = make_tt("alice", "knows", "bob");
941 assert_eq!(tt1.deterministic_hash(), tt2.deterministic_hash());
942 }
943
944 #[test]
945 fn test_hash_different_for_different_terms() {
946 let tt1 = make_tt("alice", "knows", "bob");
947 let tt2 = make_tt("alice", "knows", "carol");
948 assert_ne!(tt1.deterministic_hash(), tt2.deterministic_hash());
950 }
951
952 #[test]
953 fn test_structural_equality() {
954 let tt1 = make_tt("alice", "knows", "bob");
955 let tt2 = make_tt("alice", "knows", "bob");
956 assert!(tt1.structurally_equal(&tt2));
957 }
958
959 #[test]
960 fn test_hash_in_hashset() {
961 use std::collections::HashSet;
962 let mut set = HashSet::new();
963 set.insert(make_tt("alice", "knows", "bob"));
964 set.insert(make_tt("alice", "knows", "bob")); set.insert(make_tt("alice", "knows", "carol"));
966 assert_eq!(set.len(), 2);
967 }
968
969 #[test]
972 fn test_ordering() {
973 let tt1 = make_tt("a", "b", "c");
974 let tt2 = make_tt("a", "b", "d");
975 assert!(tt1 < tt2);
976 }
977
978 #[test]
979 fn test_sort_triple_terms() {
980 let mut terms = [
981 make_tt("c", "p", "o"),
982 make_tt("a", "p", "o"),
983 make_tt("b", "p", "o"),
984 ];
985 terms.sort();
986 assert_eq!(terms[0], make_tt("a", "p", "o"));
987 assert_eq!(terms[1], make_tt("b", "p", "o"));
988 assert_eq!(terms[2], make_tt("c", "p", "o"));
989 }
990
991 #[test]
994 fn test_with_subject() {
995 let tt = make_tt("alice", "knows", "bob").with_subject(example_iri("carol"));
996 assert_eq!(tt.subject(), &Subject::NamedNode(example_iri("carol")));
997 }
998
999 #[test]
1000 fn test_with_predicate() {
1001 let tt = make_tt("alice", "knows", "bob").with_predicate(example_iri("likes"));
1002 assert_eq!(tt.predicate(), &Predicate::NamedNode(example_iri("likes")));
1003 }
1004
1005 #[test]
1006 fn test_with_object() {
1007 let tt = make_tt("alice", "knows", "bob").with_object(Literal::new("hello"));
1008 assert!(tt.has_literal_object());
1009 }
1010
1011 #[test]
1014 fn test_triple_term_ref() {
1015 let tt = make_tt("alice", "knows", "bob");
1016 let ttref = TripleTermRef::from_triple_term(&tt);
1017 assert_eq!(ttref.subject(), tt.subject());
1018 assert_eq!(ttref.predicate(), tt.predicate());
1019 assert_eq!(ttref.object(), tt.object());
1020 }
1021
1022 #[test]
1023 fn test_triple_term_ref_to_owned() {
1024 let tt = make_tt("alice", "knows", "bob");
1025 let ttref = TripleTermRef::from_triple_term(&tt);
1026 let owned = ttref.to_owned();
1027 assert_eq!(owned, tt);
1028 }
1029
1030 #[test]
1031 fn test_triple_term_ref_display() {
1032 let tt = make_tt("alice", "knows", "bob");
1033 let ttref = TripleTermRef::from_triple_term(&tt);
1034 let display = ttref.to_string();
1035 assert!(display.contains("alice"));
1036 }
1037
1038 #[test]
1041 fn test_set_insert_and_len() {
1042 let mut set = TripleTermSet::new();
1043 assert!(set.is_empty());
1044 assert!(set.insert(make_tt("alice", "knows", "bob")));
1045 assert_eq!(set.len(), 1);
1046 assert!(!set.is_empty());
1047 }
1048
1049 #[test]
1050 fn test_set_no_duplicates() {
1051 let mut set = TripleTermSet::new();
1052 assert!(set.insert(make_tt("alice", "knows", "bob")));
1053 assert!(!set.insert(make_tt("alice", "knows", "bob")));
1054 assert_eq!(set.len(), 1);
1055 }
1056
1057 #[test]
1058 fn test_set_contains() {
1059 let mut set = TripleTermSet::new();
1060 let tt = make_tt("alice", "knows", "bob");
1061 set.insert(tt.clone());
1062 assert!(set.contains(&tt));
1063 assert!(!set.contains(&make_tt("alice", "knows", "carol")));
1064 }
1065
1066 #[test]
1067 fn test_set_remove() {
1068 let mut set = TripleTermSet::new();
1069 let tt = make_tt("alice", "knows", "bob");
1070 set.insert(tt.clone());
1071 assert!(set.remove(&tt));
1072 assert!(set.is_empty());
1073 assert!(!set.remove(&tt)); }
1075
1076 #[test]
1077 fn test_set_filter_by_predicate() {
1078 let mut set = TripleTermSet::new();
1079 set.insert(make_tt("alice", "knows", "bob"));
1080 set.insert(make_tt("alice", "likes", "carol"));
1081 set.insert(make_tt("bob", "knows", "carol"));
1082
1083 let filtered = set.filter_by_predicate("http://example.org/knows");
1084 assert_eq!(filtered.len(), 2);
1085 }
1086
1087 #[test]
1088 fn test_set_filter_by_subject() {
1089 let mut set = TripleTermSet::new();
1090 set.insert(make_tt("alice", "knows", "bob"));
1091 set.insert(make_tt("alice", "likes", "carol"));
1092 set.insert(make_tt("bob", "knows", "carol"));
1093
1094 let filtered = set.filter_by_subject_iri("http://example.org/alice");
1095 assert_eq!(filtered.len(), 2);
1096 }
1097
1098 #[test]
1099 fn test_set_ground_terms() {
1100 let mut set = TripleTermSet::new();
1101 set.insert(make_tt("alice", "knows", "bob"));
1102 let var = Variable::new("x").expect("valid var");
1103 set.insert(TripleTerm::new(
1104 var,
1105 example_iri("knows"),
1106 example_iri("bob"),
1107 ));
1108
1109 let ground = set.ground_terms();
1110 assert_eq!(ground.len(), 1);
1111 }
1112
1113 #[test]
1114 fn test_set_to_triples() {
1115 let mut set = TripleTermSet::new();
1116 set.insert(make_tt("alice", "knows", "bob"));
1117 set.insert(make_tt("alice", "knows", "carol"));
1118 let triples = set.to_triples();
1119 assert_eq!(triples.len(), 2);
1120 }
1121
1122 #[test]
1123 fn test_set_from_iterator() {
1124 let terms = vec![
1125 make_tt("alice", "knows", "bob"),
1126 make_tt("alice", "knows", "bob"), make_tt("alice", "knows", "carol"),
1128 ];
1129 let set: TripleTermSet = terms.into_iter().collect();
1130 assert_eq!(set.len(), 2);
1131 }
1132
1133 #[test]
1134 fn test_set_into_iterator() {
1135 let mut set = TripleTermSet::new();
1136 set.insert(make_tt("alice", "knows", "bob"));
1137 set.insert(make_tt("alice", "knows", "carol"));
1138 let collected: Vec<_> = set.into_iter().collect();
1139 assert_eq!(collected.len(), 2);
1140 }
1141
1142 #[test]
1143 fn test_set_drain() {
1144 let mut set = TripleTermSet::new();
1145 set.insert(make_tt("a", "b", "c"));
1146 set.insert(make_tt("d", "e", "f"));
1147 let drained: Vec<_> = set.drain().collect();
1148 assert_eq!(drained.len(), 2);
1149 assert!(set.is_empty());
1150 }
1151
1152 #[test]
1155 fn test_rdf_term_as_str() {
1156 let tt = make_tt("alice", "knows", "bob");
1157 assert_eq!(tt.as_str(), "<<triple-term>>");
1158 }
1159
1160 #[test]
1161 fn test_rdf_term_is_quoted_triple() {
1162 let tt = make_tt("alice", "knows", "bob");
1163 assert!(tt.is_quoted_triple());
1164 assert!(!tt.is_named_node());
1165 assert!(!tt.is_blank_node());
1166 assert!(!tt.is_literal());
1167 assert!(!tt.is_variable());
1168 }
1169
1170 #[test]
1173 fn test_iri_count_all_iris() {
1174 let tt = make_tt("alice", "knows", "bob");
1175 assert_eq!(tt.iri_count(), 3);
1176 }
1177
1178 #[test]
1179 fn test_iri_count_with_literal() {
1180 let tt = TripleTerm::new(
1181 example_iri("alice"),
1182 example_iri("name"),
1183 Literal::new("Alice"),
1184 );
1185 assert_eq!(tt.iri_count(), 2);
1186 }
1187
1188 #[test]
1189 fn test_iri_count_with_blank_and_variable() {
1190 let bn = BlankNode::new("b1").expect("valid blank node");
1191 let var = Variable::new("x").expect("valid var");
1192 let tt = TripleTerm::new(bn, example_iri("type"), var);
1193 assert_eq!(tt.iri_count(), 1);
1194 }
1195
1196 #[test]
1199 fn test_into_parts() {
1200 let tt = make_tt("alice", "knows", "bob");
1201 let (s, p, o) = tt.into_parts();
1202 assert_eq!(s, Subject::NamedNode(example_iri("alice")));
1203 assert_eq!(p, Predicate::NamedNode(example_iri("knows")));
1204 assert_eq!(o, Object::NamedNode(example_iri("bob")));
1205 }
1206
1207 #[test]
1210 fn test_serde_roundtrip() {
1211 let tt = make_tt("alice", "knows", "bob");
1212 let json = serde_json::to_string(&tt).expect("serialize");
1213 let deserialized: TripleTerm = serde_json::from_str(&json).expect("deserialize");
1214 assert_eq!(deserialized, tt);
1215 }
1216
1217 #[test]
1218 fn test_serde_with_literal() {
1219 let tt = TripleTerm::new(
1220 example_iri("alice"),
1221 example_iri("name"),
1222 Literal::new("Alice"),
1223 );
1224 let json = serde_json::to_string(&tt).expect("serialize");
1225 let deserialized: TripleTerm = serde_json::from_str(&json).expect("deserialize");
1226 assert_eq!(deserialized, tt);
1227 }
1228
1229 #[test]
1232 fn test_clone() {
1233 let tt = make_tt("alice", "knows", "bob");
1234 let tt2 = tt.clone();
1235 assert_eq!(tt, tt2);
1236 }
1237
1238 #[test]
1239 fn test_debug_format() {
1240 let tt = make_tt("alice", "knows", "bob");
1241 let debug = format!("{:?}", tt);
1242 assert!(debug.contains("TripleTerm"));
1243 }
1244
1245 #[test]
1246 fn test_with_capacity() {
1247 let set = TripleTermSet::with_capacity(100);
1248 assert!(set.is_empty());
1249 assert_eq!(set.len(), 0);
1250 }
1251}