Skip to main content

oxirs_core/model/
triple.rs

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