Skip to main content

oxirs_core/model/
triple.rs

1//! RDF Triple implementation
2
3use crate::model::star::QuotedTriple;
4use crate::model::RdfTerm;
5use crate::model::{BlankNode, Literal, NamedNode, Object, Predicate, Subject, Variable};
6use serde::{Deserialize, Serialize};
7use std::fmt;
8use std::hash::Hash;
9
10/// An RDF Triple
11///
12/// Represents an RDF statement with subject, predicate, and object.
13/// This is the fundamental unit of RDF data, expressing a single fact
14/// about a resource in the form "subject predicate object".
15///
16/// # Examples
17///
18/// ```rust
19/// use oxirs_core::model::{Triple, NamedNode, Literal};
20///
21/// // Create a simple triple: <http://example.org/alice> <http://example.org/name> "Alice"
22/// let triple = Triple::new(
23///     NamedNode::new("http://example.org/alice").expect("valid IRI"),
24///     NamedNode::new("http://example.org/name").expect("valid IRI"),
25///     Literal::new("Alice"),
26/// );
27///
28/// // Access components
29/// println!("Subject: {}", triple.subject());
30/// println!("Predicate: {}", triple.predicate());
31/// println!("Object: {}", triple.object());
32/// ```
33///
34/// # RDF Specification
35///
36/// According to RDF 1.2:
37/// - The **subject** can be a Named Node (IRI), Blank Node, or Variable
38/// - The **predicate** must be a Named Node (IRI) or Variable  
39/// - The **object** can be a Named Node (IRI), Blank Node, Literal, or Variable
40///
41/// Implements ordering for use in BTree indexes for efficient storage and retrieval.
42/// Triples are ordered lexicographically by subject, then predicate, then object.
43#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
44pub struct Triple {
45    subject: Subject,
46    predicate: Predicate,
47    object: Object,
48}
49
50impl Triple {
51    /// Creates a new RDF triple with the given subject, predicate, and object
52    ///
53    /// # Arguments
54    ///
55    /// * `subject` - The subject of the triple (Named Node, Blank Node, or Variable)
56    /// * `predicate` - The predicate of the triple (Named Node or Variable)
57    /// * `object` - The object of the triple (Named Node, Blank Node, Literal, or Variable)
58    ///
59    /// # Examples
60    ///
61    /// ```rust
62    /// use oxirs_core::model::{Triple, NamedNode, BlankNode, Literal};
63    ///
64    /// // Triple with all Named Nodes
65    /// let triple1 = Triple::new(
66    ///     NamedNode::new("http://example.org/alice").expect("valid IRI"),
67    ///     NamedNode::new("http://example.org/knows").expect("valid IRI"),
68    ///     NamedNode::new("http://example.org/bob").expect("valid IRI"),
69    /// );
70    ///
71    /// // Triple with a literal object
72    /// let triple2 = Triple::new(
73    ///     NamedNode::new("http://example.org/alice").expect("valid IRI"),
74    ///     NamedNode::new("http://example.org/name").expect("valid IRI"),
75    ///     Literal::new("Alice"),
76    /// );
77    ///
78    /// // Triple with a blank node subject
79    /// let triple3 = Triple::new(
80    ///     BlankNode::new("b1").expect("valid blank node id"),
81    ///     NamedNode::new("http://example.org/type").expect("valid IRI"),
82    ///     NamedNode::new("http://example.org/Person").expect("valid IRI"),
83    /// );
84    /// ```
85    pub fn new(
86        subject: impl Into<Subject>,
87        predicate: impl Into<Predicate>,
88        object: impl Into<Object>,
89    ) -> Self {
90        Triple {
91            subject: subject.into(),
92            predicate: predicate.into(),
93            object: object.into(),
94        }
95    }
96
97    /// Returns a reference to this triple
98    pub fn as_ref(&self) -> TripleRef<'_> {
99        TripleRef::from(self)
100    }
101
102    /// Returns the subject of this triple
103    pub fn subject(&self) -> &Subject {
104        &self.subject
105    }
106
107    /// Returns the predicate of this triple
108    pub fn predicate(&self) -> &Predicate {
109        &self.predicate
110    }
111
112    /// Returns the object of this triple
113    pub fn object(&self) -> &Object {
114        &self.object
115    }
116
117    /// Decomposes the triple into its components
118    pub fn into_parts(self) -> (Subject, Predicate, Object) {
119        (self.subject, self.predicate, self.object)
120    }
121
122    /// Returns true if this triple contains any variables
123    pub fn has_variables(&self) -> bool {
124        matches!(self.subject, Subject::Variable(_))
125            || matches!(self.predicate, Predicate::Variable(_))
126            || matches!(self.object, Object::Variable(_))
127    }
128
129    /// Returns true if this triple is ground (contains no variables)
130    pub fn is_ground(&self) -> bool {
131        !self.has_variables()
132    }
133
134    /// Returns true if this triple matches the given pattern
135    ///
136    /// None values in the pattern act as wildcards matching any term.
137    pub fn matches_pattern(
138        &self,
139        subject: Option<&Subject>,
140        predicate: Option<&Predicate>,
141        object: Option<&Object>,
142    ) -> bool {
143        if let Some(s) = subject {
144            if &self.subject != s {
145                return false;
146            }
147        }
148
149        if let Some(p) = predicate {
150            if &self.predicate != p {
151                return false;
152            }
153        }
154
155        if let Some(o) = object {
156            if &self.object != o {
157                return false;
158            }
159        }
160
161        true
162    }
163
164    /// Returns the canonical order of this triple for sorting
165    ///
166    /// This enables efficient storage in BTree-based indexes.
167    /// Order: Subject -> Predicate -> Object
168    #[allow(dead_code)]
169    fn canonical_ordering(&self) -> (u8, &str, u8, &str, u8, &str) {
170        let subject_ord = match &self.subject {
171            Subject::NamedNode(_) => 0,
172            Subject::BlankNode(_) => 1,
173            Subject::Variable(_) => 2,
174            Subject::QuotedTriple(_) => 3,
175        };
176
177        let predicate_ord = match &self.predicate {
178            Predicate::NamedNode(_) => 0,
179            Predicate::Variable(_) => 1,
180        };
181
182        let object_ord = match &self.object {
183            Object::NamedNode(_) => 0,
184            Object::BlankNode(_) => 1,
185            Object::Literal(_) => 2,
186            Object::Variable(_) => 3,
187            Object::QuotedTriple(_) => 4,
188        };
189
190        (
191            subject_ord,
192            self.subject_str(),
193            predicate_ord,
194            self.predicate_str(),
195            object_ord,
196            self.object_str(),
197        )
198    }
199
200    /// Returns the subject as a string for ordering
201    #[allow(dead_code)]
202    fn subject_str(&self) -> &str {
203        match &self.subject {
204            Subject::NamedNode(n) => n.as_str(),
205            Subject::BlankNode(b) => b.as_str(),
206            Subject::Variable(v) => v.as_str(),
207            Subject::QuotedTriple(_) => "<<quoted-triple>>",
208        }
209    }
210
211    /// Returns the predicate as a string for ordering
212    #[allow(dead_code)]
213    fn predicate_str(&self) -> &str {
214        match &self.predicate {
215            Predicate::NamedNode(n) => n.as_str(),
216            Predicate::Variable(v) => v.as_str(),
217        }
218    }
219
220    /// Returns the object as a string for ordering
221    #[allow(dead_code)]
222    fn object_str(&self) -> &str {
223        match &self.object {
224            Object::NamedNode(n) => n.as_str(),
225            Object::BlankNode(b) => b.as_str(),
226            Object::Literal(l) => l.as_str(),
227            Object::Variable(v) => v.as_str(),
228            Object::QuotedTriple(_) => "<<quoted-triple>>",
229        }
230    }
231}
232
233impl fmt::Display for Triple {
234    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
235        write!(f, "{} {} {} .", self.subject, self.predicate, self.object)
236    }
237}
238
239// Display implementations for term unions
240impl fmt::Display for Subject {
241    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
242        match self {
243            Subject::NamedNode(n) => write!(f, "{n}"),
244            Subject::BlankNode(b) => write!(f, "{b}"),
245            Subject::Variable(v) => write!(f, "{v}"),
246            Subject::QuotedTriple(qt) => write!(f, "{qt}"),
247        }
248    }
249}
250
251impl fmt::Display for Predicate {
252    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
253        match self {
254            Predicate::NamedNode(n) => write!(f, "{n}"),
255            Predicate::Variable(v) => write!(f, "{v}"),
256        }
257    }
258}
259
260impl fmt::Display for Object {
261    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
262        match self {
263            Object::NamedNode(n) => write!(f, "{n}"),
264            Object::BlankNode(b) => write!(f, "{b}"),
265            Object::Literal(l) => write!(f, "{l}"),
266            Object::Variable(v) => write!(f, "{v}"),
267            Object::QuotedTriple(qt) => write!(f, "{qt}"),
268        }
269    }
270}
271
272/// A borrowed triple reference for zero-copy operations
273#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
274pub struct TripleRef<'a> {
275    subject: SubjectRef<'a>,
276    predicate: PredicateRef<'a>,
277    object: ObjectRef<'a>,
278}
279
280impl<'a> TripleRef<'a> {
281    /// Creates a new triple reference
282    pub fn new(
283        subject: SubjectRef<'a>,
284        predicate: PredicateRef<'a>,
285        object: ObjectRef<'a>,
286    ) -> Self {
287        TripleRef {
288            subject,
289            predicate,
290            object,
291        }
292    }
293
294    /// Returns the subject
295    pub fn subject(&self) -> SubjectRef<'a> {
296        self.subject
297    }
298
299    /// Returns the predicate
300    pub fn predicate(&self) -> PredicateRef<'a> {
301        self.predicate
302    }
303
304    /// Returns the object
305    pub fn object(&self) -> ObjectRef<'a> {
306        self.object
307    }
308
309    /// Converts to an owned triple
310    pub fn to_owned(&self) -> Triple {
311        Triple {
312            subject: self.subject.to_owned(),
313            predicate: self.predicate.to_owned(),
314            object: self.object.to_owned(),
315        }
316    }
317
318    /// Converts to an owned triple (alias for to_owned)
319    pub fn into_owned(self) -> Triple {
320        self.to_owned()
321    }
322
323    /// Creates a QuadRef from this triple with the specified graph
324    pub fn in_graph(
325        self,
326        graph_name: Option<&'a crate::model::NamedNode>,
327    ) -> crate::model::QuadRef<'a> {
328        let graph_ref = match graph_name {
329            Some(node) => crate::model::GraphNameRef::NamedNode(node),
330            None => crate::model::GraphNameRef::DefaultGraph,
331        };
332        crate::model::QuadRef::new(self.subject, self.predicate, self.object, graph_ref)
333    }
334}
335
336impl<'a> fmt::Display for TripleRef<'a> {
337    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
338        write!(f, "{} {} {} .", self.subject, self.predicate, self.object)
339    }
340}
341
342/// Borrowed subject reference
343#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
344pub enum SubjectRef<'a> {
345    NamedNode(&'a NamedNode),
346    BlankNode(&'a BlankNode),
347    Variable(&'a Variable),
348    /// A quoted triple (RDF-star / RDF 1.2)
349    QuotedTriple(&'a QuotedTriple),
350}
351
352impl<'a> SubjectRef<'a> {
353    /// Converts to an owned subject
354    pub fn to_owned(&self) -> Subject {
355        match self {
356            SubjectRef::NamedNode(n) => Subject::NamedNode((*n).clone()),
357            SubjectRef::BlankNode(b) => Subject::BlankNode((*b).clone()),
358            SubjectRef::Variable(v) => Subject::Variable((*v).clone()),
359            SubjectRef::QuotedTriple(qt) => Subject::QuotedTriple(Box::new((*qt).clone())),
360        }
361    }
362}
363
364impl<'a> fmt::Display for SubjectRef<'a> {
365    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
366        match self {
367            SubjectRef::NamedNode(n) => write!(f, "{n}"),
368            SubjectRef::BlankNode(b) => write!(f, "{b}"),
369            SubjectRef::Variable(v) => write!(f, "{v}"),
370            SubjectRef::QuotedTriple(qt) => write!(f, "{qt}"),
371        }
372    }
373}
374
375/// Borrowed predicate reference
376#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
377pub enum PredicateRef<'a> {
378    NamedNode(&'a NamedNode),
379    Variable(&'a Variable),
380}
381
382impl<'a> PredicateRef<'a> {
383    /// Converts to an owned predicate
384    pub fn to_owned(&self) -> Predicate {
385        match self {
386            PredicateRef::NamedNode(n) => Predicate::NamedNode((*n).clone()),
387            PredicateRef::Variable(v) => Predicate::Variable((*v).clone()),
388        }
389    }
390}
391
392impl<'a> fmt::Display for PredicateRef<'a> {
393    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
394        match self {
395            PredicateRef::NamedNode(n) => write!(f, "{n}"),
396            PredicateRef::Variable(v) => write!(f, "{v}"),
397        }
398    }
399}
400
401/// Borrowed object reference
402#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
403pub enum ObjectRef<'a> {
404    NamedNode(&'a NamedNode),
405    BlankNode(&'a BlankNode),
406    Literal(&'a Literal),
407    Variable(&'a Variable),
408    /// A quoted triple (RDF-star / RDF 1.2)
409    QuotedTriple(&'a QuotedTriple),
410}
411
412impl<'a> ObjectRef<'a> {
413    /// Converts to an owned object
414    pub fn to_owned(&self) -> Object {
415        match self {
416            ObjectRef::NamedNode(n) => Object::NamedNode((*n).clone()),
417            ObjectRef::BlankNode(b) => Object::BlankNode((*b).clone()),
418            ObjectRef::Literal(l) => Object::Literal((*l).clone()),
419            ObjectRef::Variable(v) => Object::Variable((*v).clone()),
420            ObjectRef::QuotedTriple(qt) => Object::QuotedTriple(Box::new((*qt).clone())),
421        }
422    }
423}
424
425impl<'a> fmt::Display for ObjectRef<'a> {
426    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
427        match self {
428            ObjectRef::NamedNode(n) => write!(f, "{n}"),
429            ObjectRef::BlankNode(b) => write!(f, "{b}"),
430            ObjectRef::Literal(l) => write!(f, "{l}"),
431            ObjectRef::Variable(v) => write!(f, "{v}"),
432            ObjectRef::QuotedTriple(qt) => write!(f, "{qt}"),
433        }
434    }
435}
436
437// Conversion implementations
438impl<'a> From<&'a Subject> for SubjectRef<'a> {
439    fn from(subject: &'a Subject) -> Self {
440        match subject {
441            Subject::NamedNode(n) => SubjectRef::NamedNode(n),
442            Subject::BlankNode(b) => SubjectRef::BlankNode(b),
443            Subject::Variable(v) => SubjectRef::Variable(v),
444            Subject::QuotedTriple(qt) => SubjectRef::QuotedTriple(qt),
445        }
446    }
447}
448
449impl<'a> From<&'a Predicate> for PredicateRef<'a> {
450    fn from(predicate: &'a Predicate) -> Self {
451        match predicate {
452            Predicate::NamedNode(n) => PredicateRef::NamedNode(n),
453            Predicate::Variable(v) => PredicateRef::Variable(v),
454        }
455    }
456}
457
458impl<'a> From<&'a Object> for ObjectRef<'a> {
459    fn from(object: &'a Object) -> Self {
460        match object {
461            Object::NamedNode(n) => ObjectRef::NamedNode(n),
462            Object::BlankNode(b) => ObjectRef::BlankNode(b),
463            Object::Literal(l) => ObjectRef::Literal(l),
464            Object::Variable(v) => ObjectRef::Variable(v),
465            Object::QuotedTriple(qt) => ObjectRef::QuotedTriple(qt),
466        }
467    }
468}
469
470impl<'a> From<&'a Triple> for TripleRef<'a> {
471    fn from(triple: &'a Triple) -> Self {
472        TripleRef {
473            subject: triple.subject().into(),
474            predicate: triple.predicate().into(),
475            object: triple.object().into(),
476        }
477    }
478}
479
480impl<'a> From<TripleRef<'a>> for Triple {
481    fn from(triple_ref: TripleRef<'a>) -> Self {
482        triple_ref.to_owned()
483    }
484}
485
486#[cfg(test)]
487mod tests {
488    use super::*;
489    use crate::model::{Literal, NamedNode};
490
491    #[test]
492    fn test_triple_creation() {
493        let subject = NamedNode::new("http://example.org/subject").expect("valid IRI");
494        let predicate = NamedNode::new("http://example.org/predicate").expect("valid IRI");
495        let object = Literal::new("object");
496
497        let triple = Triple::new(subject.clone(), predicate.clone(), object.clone());
498
499        assert!(triple.is_ground());
500        assert!(!triple.has_variables());
501    }
502
503    #[test]
504    fn test_triple_with_variable() {
505        let subject = Variable::new("x").expect("valid variable name");
506        let predicate = NamedNode::new("http://example.org/predicate").expect("valid IRI");
507        let object = Literal::new("object");
508
509        let triple = Triple::new(subject, predicate, object);
510
511        assert!(!triple.is_ground());
512        assert!(triple.has_variables());
513    }
514
515    #[test]
516    fn test_triple_display() {
517        let subject = NamedNode::new("http://example.org/s").expect("valid IRI");
518        let predicate = NamedNode::new("http://example.org/p").expect("valid IRI");
519        let object = Literal::new("o");
520
521        let triple = Triple::new(subject, predicate, object);
522        let display_str = format!("{triple}");
523
524        assert!(display_str.contains("http://example.org/s"));
525        assert!(display_str.contains("http://example.org/p"));
526        assert!(display_str.contains("\"o\""));
527        assert!(display_str.ends_with(" ."));
528    }
529
530    #[test]
531    fn test_triple_ref() {
532        let subject = NamedNode::new("http://example.org/s").expect("valid IRI");
533        let predicate = NamedNode::new("http://example.org/p").expect("valid IRI");
534        let object = Literal::new("o");
535
536        let triple = Triple::new(subject, predicate, object);
537        let triple_ref = TripleRef::from(&triple);
538        let triple_owned = triple_ref.to_owned();
539
540        assert_eq!(triple, triple_owned);
541    }
542
543    #[test]
544    fn test_pattern_matching() {
545        let subject = NamedNode::new("http://example.org/s").expect("valid IRI");
546        let predicate = NamedNode::new("http://example.org/p").expect("valid IRI");
547        let object = Literal::new("o");
548
549        let triple = Triple::new(subject.clone(), predicate.clone(), object.clone());
550
551        // Test exact match
552        assert!(triple.matches_pattern(
553            Some(&Subject::NamedNode(subject.clone())),
554            Some(&Predicate::NamedNode(predicate.clone())),
555            Some(&Object::Literal(object.clone()))
556        ));
557
558        // Test wildcard matches
559        assert!(triple.matches_pattern(None, None, None));
560        assert!(triple.matches_pattern(Some(&Subject::NamedNode(subject.clone())), None, None));
561        assert!(triple.matches_pattern(None, Some(&Predicate::NamedNode(predicate.clone())), None));
562        assert!(triple.matches_pattern(None, None, Some(&Object::Literal(object.clone()))));
563
564        // Test non-matches
565        let different_subject = NamedNode::new("http://example.org/different").expect("valid IRI");
566        assert!(!triple.matches_pattern(Some(&Subject::NamedNode(different_subject)), None, None));
567    }
568
569    #[test]
570    fn test_triple_ordering() {
571        let subject1 = NamedNode::new("http://example.org/a").expect("valid IRI");
572        let subject2 = NamedNode::new("http://example.org/b").expect("valid IRI");
573        let predicate = NamedNode::new("http://example.org/p").expect("valid IRI");
574        let object = Literal::new("o");
575
576        let triple1 = Triple::new(subject1, predicate.clone(), object.clone());
577        let triple2 = Triple::new(subject2, predicate, object);
578
579        assert!(triple1 < triple2);
580
581        let mut triples = vec![triple2.clone(), triple1.clone()];
582        triples.sort();
583        assert_eq!(triples, vec![triple1, triple2]);
584    }
585
586    #[test]
587    fn regression_rdf_star_as_ref_does_not_panic() {
588        use crate::model::star::QuotedTriple;
589
590        // Build an RDF-star triple: << <s> <p> "o" >> <asserts> "true"
591        let inner = Triple::new(
592            NamedNode::new("http://example.org/s").expect("valid IRI"),
593            NamedNode::new("http://example.org/p").expect("valid IRI"),
594            Literal::new("o"),
595        );
596        let qt = QuotedTriple::new(inner);
597        let outer = Triple::new(
598            Subject::QuotedTriple(Box::new(qt.clone())),
599            NamedNode::new("http://example.org/asserts").expect("valid IRI"),
600            Object::QuotedTriple(Box::new(qt)),
601        );
602
603        // Previously this panicked ("QuotedTriple not supported in SubjectRef").
604        let borrowed = outer.as_ref();
605        assert!(matches!(borrowed.subject(), SubjectRef::QuotedTriple(_)));
606        assert!(matches!(borrowed.object(), ObjectRef::QuotedTriple(_)));
607
608        // Zero-copy view must round-trip back to an equal owned triple.
609        let round = borrowed.to_owned();
610        assert_eq!(outer, round);
611
612        // Display must render RDF-star syntax without panicking.
613        let rendered = format!("{}", borrowed.subject());
614        assert!(rendered.contains("<<"));
615        assert!(rendered.contains("http://example.org/s"));
616    }
617
618    #[test]
619    fn test_triple_serialization() {
620        let subject = NamedNode::new("http://example.org/s").expect("valid IRI");
621        let predicate = NamedNode::new("http://example.org/p").expect("valid IRI");
622        let object = Literal::new("o");
623
624        let triple = Triple::new(subject, predicate, object);
625        let json = serde_json::to_string(&triple).expect("construction should succeed");
626        let deserialized: Triple =
627            serde_json::from_str(&json).expect("construction should succeed");
628
629        assert_eq!(triple, deserialized);
630    }
631}