Skip to main content

rudof_rdf/rdf_core/term/
triple.rs

1use crate::rdf_core::{
2    RDFError, Rdf,
3    term::{
4        Iri, IriOrBlankNode, Term, TermKind,
5        literal::{ConcreteLiteral, Lang, NumericLiteral},
6    },
7};
8use iri_s::IriS;
9use prefixmap::IriRef;
10use serde::{Deserialize, Serialize};
11use std::{
12    fmt::{Debug, Display},
13    hash::Hash,
14};
15
16/// Represents an RDF triple.
17///
18/// An RDF triple consists of three components: a subject, a predicate, and an object.
19///
20/// # Type Parameters
21///
22/// * `S` - The subject type, which must implement `Subject`
23/// * `P` - The predicate type, which must implement `Iri`
24/// * `O` - The object type, which must implement `Term`
25pub trait Triple<S, P, O>: Debug + Clone + Display
26where
27    S: Subject,
28    P: Iri,
29    O: Term,
30{
31    /// Constructs a new RDF triple from the given components.
32    ///
33    /// # Parameters
34    ///
35    /// * `subj` - The subject of the triple, convertible to type `S`
36    /// * `pred` - The predicate of the triple, convertible to type `P`
37    /// * `obj` - The object of the triple, convertible to type `O`
38    fn new(subj: impl Into<S>, pred: impl Into<P>, obj: impl Into<O>) -> Self;
39
40    /// Returns a reference to the subject of this triple.
41    fn subj(&self) -> &S;
42
43    /// Returns a reference to the predicate of this triple.
44    fn pred(&self) -> &P;
45
46    /// Returns a reference to the object of this triple.
47    fn obj(&self) -> &O;
48
49    /// Consumes the triple and returns its components as a tuple.
50    ///
51    /// This method takes ownership of the triple and returns `(subject, predicate, object)`,
52    /// allowing you to extract the individual components without cloning.
53    fn into_components(self) -> (S, P, O);
54
55    /// Consumes the triple and returns only the subject.
56    fn into_subject(self) -> S {
57        self.into_components().0
58    }
59
60    /// Consumes the triple and returns only the predicate.
61    fn into_predicate(self) -> P {
62        self.into_components().1
63    }
64
65    /// Consumes the triple and returns only the object.
66    fn into_object(self) -> O {
67        self.into_components().2
68    }
69}
70
71/// A concrete implementation of an RDF triple for a specific RDF model.
72///
73/// # Type Parameters
74///
75/// * `R` - The RDF implementation type that defines the specific types for subjects, predicates, and objects through its associated types
76pub struct ConcreteTriple<R>
77where
78    R: Rdf,
79{
80    subj: R::Subject,
81    pred: R::IRI,
82    obj: R::Term,
83}
84
85impl<R> ConcreteTriple<R>
86where
87    R: Rdf,
88{
89    /// Creates a new concrete triple from owned components.
90    ///
91    /// # Parameters
92    ///
93    /// * `subj` - The subject component from the RDF model `R`
94    /// * `pred` - The predicate component from the RDF model `R`
95    /// * `obj` - The object component from the RDF model `R`
96    pub fn new(subj: R::Subject, pred: R::IRI, obj: R::Term) -> Self {
97        ConcreteTriple { subj, pred, obj }
98    }
99
100    /// Returns a reference to the subject component.
101    pub fn subj(&self) -> &R::Subject {
102        &self.subj
103    }
104
105    /// Returns a reference to the predicate component.
106    pub fn pred(&self) -> &R::IRI {
107        &self.pred
108    }
109
110    /// Returns a reference to the object component.
111    pub fn obj(&self) -> &R::Term {
112        &self.obj
113    }
114
115    /// Converts this triple from one RDF implementation to another.
116    ///
117    /// # Type Parameters
118    ///
119    /// * `T` - The target RDF implementation type
120    ///
121    /// # Trait Bounds
122    ///
123    /// Requires that the target RDF model's types can be converted from the
124    /// source RDF model's types:
125    /// - `T::Subject: From<R::Subject>` - Subject conversion
126    /// - `T::Term: From<R::Term>` - Term conversion
127    /// - `T::IRI: From<R::IRI>` - IRI conversion
128    pub fn cnv<T: Rdf>(self) -> ConcreteTriple<T>
129    where
130        T::Subject: From<R::Subject>,
131        T::Term: From<R::Term>,
132        T::IRI: From<R::IRI>,
133    {
134        ConcreteTriple {
135            subj: T::Subject::from(self.subj),
136            pred: T::IRI::from(self.pred),
137            obj: T::Term::from(self.obj),
138        }
139    }
140}
141
142/// Represents the subject position of an RDF triple.
143///
144/// In RDF, a subject can be an IRI, a blank node, or (in RDF-star) a triple.
145/// This trait defines the common behavior for all types that can appear as
146/// subjects in RDF statements.
147pub trait Subject: Debug + Display + PartialEq + Clone + Eq + Hash {
148    /// Returns the kind of RDF term this subject represents.
149    ///
150    /// This method allows distinguishing between IRIs, blank nodes, and quoted triples at runtime.
151    fn kind(&self) -> TermKind;
152
153    /// Returns `true` if this subject is an IRI.
154    fn is_iri(&self) -> bool {
155        self.kind() == TermKind::Iri
156    }
157
158    /// Returns `true` if this subject is a blank node.
159    fn is_blank_node(&self) -> bool {
160        self.kind() == TermKind::BlankNode
161    }
162
163    /// Returns `true` if this subject is a quoted triple (RDF-star).
164    fn is_triple(&self) -> bool {
165        self.kind() == TermKind::Triple
166    }
167}
168
169/// Represents an RDF object value in the object position of a triple.
170///
171/// In RDF, the object is the third component of a triple (subject-predicate-object)
172/// and can be one of four types:
173/// - **IRI**: A resource identified by an Internationalized Resource Identifier
174/// - **Blank Node**: An anonymous resource without a global identifier
175/// - **Literal**: A concrete value (string, number, date, etc.) with optional datatype/language
176/// - **Triple** (RDF-star): A quoted triple that can be nested as an object
177#[derive(Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
178pub enum Object {
179    /// An IRI (Internationalized Resource Identifier) representing a named resource.
180    Iri(IriS),
181    /// A blank node (anonymous resource) identified by a local label.
182    BlankNode(String),
183    /// A literal value with a datatype and optional language tag.
184    Literal(ConcreteLiteral),
185    /// An RDF-star quoted triple that can be used as an object.
186    ///
187    /// # Fields
188    /// - `subject`: The subject of the nested triple (IRI or blank node)
189    /// - `predicate`: The predicate of the nested triple (IRI)
190    /// - `object`: The object of the nested triple (recursively an Object)
191    Triple {
192        subject: Box<IriOrBlankNode>,
193        predicate: IriS,
194        object: Box<Object>,
195    },
196}
197
198/// ## Constructors methods
199impl Object {
200    /// Creates an IRI object from an `IriS` instance.
201    ///
202    /// # Parameters
203    /// - `iri`: The IRI to wrap as an object
204    pub fn iri(iri: IriS) -> Object {
205        Object::Iri(iri)
206    }
207
208    /// Creates a blank node object from a string identifier.
209    ///
210    /// # Parameters
211    /// - `str`: The blank node identifier
212    pub fn bnode(str: String) -> Object {
213        Object::BlankNode(str)
214    }
215
216    /// Creates a literal object from a concrete literal value.
217    ///
218    /// # Parameters
219    /// - `lit`: The concrete literal to wrap as an object
220    pub fn literal(lit: ConcreteLiteral) -> Object {
221        Object::Literal(lit)
222    }
223
224    /// Creates a string literal object from a string slice.
225    ///
226    /// # Parameters
227    /// - `str`: The string value for the literal
228    pub fn str(str: &str) -> Object {
229        Object::Literal(ConcreteLiteral::str(str))
230    }
231
232    /// Creates a boolean literal object.
233    ///
234    /// # Parameters
235    /// - `b`: The boolean value
236    pub fn boolean(b: bool) -> Object {
237        Object::Literal(ConcreteLiteral::boolean(b))
238    }
239}
240
241/// ## Accessors methods
242impl Object {
243    // Returns the length (in bytes) of this object's string representation.
244    ///
245    /// - For IRIs: the length of the IRI string
246    /// - For blank nodes: the length of the identifier
247    /// - For literals: the length of the lexical form
248    /// - For triples: the sum of all component lengths
249    pub fn length(&self) -> usize {
250        match self {
251            Object::Iri(iri) => iri.as_str().len(),
252            Object::BlankNode(bn) => bn.len(),
253            Object::Literal(lit) => lit.lexical_form().len(),
254            Object::Triple {
255                subject,
256                predicate,
257                object,
258            } => subject.as_ref().length() + predicate.as_str().len() + object.as_ref().length(),
259        }
260    }
261
262    /// Extracts the numeric value if this is a numeric literal.
263    ///
264    /// # Returns
265    /// - `Some(NumericLiteral)` if this is a numeric literal (integer, decimal, float, double)
266    /// - `None` if this is not a literal or not a numeric type
267    pub fn numeric_value(&self) -> Option<NumericLiteral> {
268        match self {
269            Object::Literal(lit) => lit.numeric_value(),
270            _ => None,
271        }
272    }
273
274    /// Returns the datatype IRI of this object if it's a literal.
275    /// # Returns
276    /// - `Some(IriRef)` if this is a literal
277    /// - `None` if this is an IRI, blank node, or triple
278    pub fn datatype(&self) -> Option<IriRef> {
279        match self {
280            Object::Literal(lit) => Some(lit.datatype()),
281            _ => None,
282        }
283    }
284
285    /// Returns the language tag if this is a language-tagged string literal.
286    ///
287    /// # Returns
288    /// - `Some(&Lang)` if this is a string literal with a language tag (e.g., "en", "es-MX")
289    /// - `None` if this is not a language-tagged literal
290    pub fn lang(&self) -> Option<&Lang> {
291        match self {
292            Object::Literal(ConcreteLiteral::StringLiteral { lang: Some(lang), .. }) => Some(lang),
293            _ => None,
294        }
295    }
296}
297
298impl Object {
299    /// ## Parsing methods
300    /// Parses a string into an RDF object, with optional base IRI resolution.
301    ///
302    /// This method attempts to parse:
303    /// - Blank nodes: strings starting with "_:"
304    /// - Literals: strings starting with '"' (not yet implemented)
305    /// - IRIs: all other strings, resolved against the base if provided
306    ///
307    /// # Parameters
308    /// - `str`: The string to parse
309    /// - `base`: Optional base IRI for resolving relative IRI references
310    ///
311    /// # Errors
312    /// - `RDFError::ParsingIri` if IRI parsing fails
313    pub fn parse(str: &str, base: Option<&str>) -> Result<Object, RDFError> {
314        if let Some(bnode_id) = str.strip_prefix("_:") {
315            Ok(Object::bnode(bnode_id.to_string()))
316        } else if str.starts_with('"') {
317            todo!()
318        } else {
319            let iri = IriS::from_str_base(str, base).map_err(|e| RDFError::ParsingIri {
320                iri: str.to_string(),
321                error: e.to_string(),
322            })?;
323            Ok(Object::iri(iri))
324        }
325    }
326}
327
328/// ## Formatting methods
329impl Object {
330    /// Formats this object using qualified names (prefixes) where possible.
331    ///
332    /// This method produces a compact representation by replacing full IRIs
333    /// with prefixed names (e.g., "rdf:type" instead of "http://www.w3.org/1999/02/22-rdf-syntax-ns#type").
334    ///
335    /// # Parameters
336    /// - `prefixmap`: A prefix map containing IRI-to-prefix mappings
337    pub fn show_qualified(&self, prefixmap: &prefixmap::PrefixMap) -> String {
338        match self {
339            Object::Iri(iri) => prefixmap.qualify(iri),
340            Object::BlankNode(bnode) => format!("_:{bnode}"),
341            Object::Literal(lit) => lit.show_qualified(prefixmap),
342            Object::Triple {
343                subject,
344                predicate,
345                object,
346            } => format!(
347                "<< {} {} {} >>",
348                subject.show_qualified(prefixmap),
349                prefixmap.qualify(predicate),
350                object.show_qualified(prefixmap)
351            ),
352        }
353    }
354}
355
356// ============================================================================
357// Trait Implementations - Conversions
358// ============================================================================
359
360/// Converts an `IriS` into an `Object::Iri`.
361///
362/// This allows IRIs to be seamlessly used where objects are expected.
363impl From<IriS> for Object {
364    fn from(iri: IriS) -> Self {
365        Object::Iri(iri)
366    }
367}
368
369/// Converts a `ConcreteLiteral` into an `Object::Literal`.
370///
371/// This allows literals to be seamlessly used where objects are expected.
372impl From<ConcreteLiteral> for Object {
373    fn from(lit: ConcreteLiteral) -> Self {
374        Object::Literal(lit)
375    }
376}
377
378/// Converts an `Object` into an `oxrdf::Term`.
379///
380/// This enables interoperability with the `oxrdf` library by converting
381/// the custom `Object` representation into oxrdf's term type.
382impl From<Object> for oxrdf::Term {
383    fn from(value: Object) -> Self {
384        match value {
385            Object::Iri(iri_s) => oxrdf::NamedNode::new_unchecked(iri_s.as_str()).into(),
386            Object::BlankNode(bnode) => oxrdf::BlankNode::new_unchecked(bnode).into(),
387            Object::Literal(literal) => oxrdf::Term::Literal(literal.into()),
388            Object::Triple { .. } => todo!(),
389        }
390    }
391}
392
393/// Attempts to convert an `oxrdf::Term` into an `Object`.
394///
395/// This enables interoperability with the `oxrdf` library by converting
396/// oxrdf's term type into the custom `Object` representation.
397/// # Errors
398/// Returns `RDFError` if the conversion fails (e.g., invalid literal format).
399impl TryFrom<oxrdf::Term> for Object {
400    type Error = RDFError;
401
402    fn try_from(value: oxrdf::Term) -> Result<Self, Self::Error> {
403        match value {
404            oxrdf::Term::NamedNode(named_node) => Ok(Object::iri(named_node.into())),
405            oxrdf::Term::BlankNode(blank_node) => Ok(Object::bnode(blank_node.into_string())),
406            oxrdf::Term::Literal(literal) => {
407                let lit: ConcreteLiteral = literal.try_into()?;
408                Ok(Object::literal(lit))
409            },
410            oxrdf::Term::Triple(triple) => {
411                let (s, p, o) = triple.into_components();
412                let object = Object::try_from(o)?;
413                let subject = IriOrBlankNode::from(s);
414                let predicate = p.into();
415                Ok(Object::Triple {
416                    subject: Box::new(subject),
417                    predicate,
418                    object: Box::new(object),
419                })
420            },
421        }
422    }
423}
424
425/// Attempts to convert an `Object` into an `oxrdf::NamedOrBlankNode`.
426///
427/// This conversion is used when an object appears in subject position
428/// (which can only be IRIs or blank nodes, not literals).
429///
430/// # Errors
431/// Returns `RDFError` for objects that cannot be subjects (literals and triples).
432impl TryFrom<Object> for oxrdf::NamedOrBlankNode {
433    type Error = RDFError;
434
435    fn try_from(value: Object) -> Result<Self, Self::Error> {
436        println!("Trying from Object: {value}");
437        match value {
438            Object::Iri(iri_s) => Ok(oxrdf::NamedNode::new_unchecked(iri_s.as_str()).into()),
439            Object::BlankNode(bnode) => Ok(oxrdf::BlankNode::new_unchecked(bnode).into()),
440            Object::Literal(_) => todo!(),
441            Object::Triple { .. } => todo!(),
442        }
443    }
444}
445
446// ============================================================================
447// Trait Implementations - Default, Display, Debug
448// ============================================================================
449
450impl<R> Display for ConcreteTriple<R>
451where
452    R: Rdf,
453{
454    /// Formats the triple as a string.
455    ///
456    /// # Parameters
457    ///
458    /// * `f` - The formatter to write to
459    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
460        write!(f, "<{},{},{}>", self.subj, self.pred, self.obj)
461    }
462}
463
464impl Default for Object {
465    /// Provides a default `Object` value (empty IRI).
466    fn default() -> Self {
467        Object::Iri(IriS::default())
468    }
469}
470
471impl Display for Object {
472    /// Formats the object for display (human-readable output).
473    ///
474    /// - IRIs: displayed as-is
475    /// - Blank nodes: prefixed with "_:"
476    /// - Literals: uses the literal's Display implementation
477    /// - Triples: not yet implemented
478    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
479        match self {
480            Object::Iri(iri) => write!(f, "{iri}"),
481            Object::BlankNode(bnode) => write!(f, "_:{bnode}"),
482            Object::Literal(lit) => write!(f, "{lit}"),
483            Object::Triple { .. } => todo!(),
484        }
485    }
486}
487
488impl Debug for Object {
489    /// Formats the object for debugging (verbose output with type information).
490    ///
491    /// Includes type tags for each variant:
492    /// - "Iri {<iri>}"
493    /// - "Bnode{<id>}"
494    /// - "Literal{<value>}"
495    /// - "Triple {<s>, <p>, <o>}"
496    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
497        match self {
498            Object::Iri(iri) => write!(f, "Iri {{{iri:?}}}"),
499            Object::BlankNode(bnode) => write!(f, "Bnode{{{bnode:?}}}"),
500            Object::Literal(lit) => write!(f, "Literal{{{lit:?}}}"),
501            Object::Triple {
502                subject,
503                predicate,
504                object,
505            } => write!(f, "Triple {{{subject:?}, {predicate:?}, {object:?}}}"),
506        }
507    }
508}
509
510// ============================================================================
511// Trait Implementations - Ordering
512// ============================================================================
513
514impl PartialOrd for Object {
515    /// Implements partial ordering for objects.
516    ///
517    /// Since `Object` implements total ordering via [`Ord`], this always returns
518    /// `Some(ordering)`.
519    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
520        Some(self.cmp(other))
521    }
522}
523
524impl Ord for Object {
525    /// Implements total ordering for objects according to RDF semantics.
526    ///
527    /// The ordering priority is: IRIs < Blank Nodes < Literals.
528    /// Within each category, standard comparison applies:
529    /// - IRIs: lexicographic ordering of IRI strings
530    /// - Blank nodes: lexicographic ordering of identifiers
531    /// - Literals: ordering defined by `ConcreteLiteral::cmp`
532    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
533        match (self, other) {
534            (Object::Iri(a), Object::Iri(b)) => a.cmp(b),
535            (Object::BlankNode(a), Object::BlankNode(b)) => a.cmp(b),
536            (Object::Literal(a), Object::Literal(b)) => a.cmp(b),
537            (Object::Iri(_), _) => std::cmp::Ordering::Less,
538            (Object::BlankNode(_), Object::Iri(_)) => std::cmp::Ordering::Greater,
539            (Object::BlankNode(_), Object::Literal(_)) => std::cmp::Ordering::Less,
540            (Object::Literal(_), _) => std::cmp::Ordering::Greater,
541            (
542                Object::BlankNode(_),
543                Object::Triple {
544                    subject: _,
545                    predicate: _,
546                    object: _,
547                },
548            ) => todo!(),
549            (
550                Object::Triple {
551                    subject: _,
552                    predicate: _,
553                    object: _,
554                },
555                Object::Iri(_iri_s),
556            ) => todo!(),
557            (
558                Object::Triple {
559                    subject: _,
560                    predicate: _,
561                    object: _,
562                },
563                Object::BlankNode(_),
564            ) => todo!(),
565            (
566                Object::Triple {
567                    subject: _,
568                    predicate: _,
569                    object: _,
570                },
571                Object::Literal(_sliteral),
572            ) => todo!(),
573            (
574                Object::Triple {
575                    subject: _subject1,
576                    predicate: _predicate1,
577                    object: _object1,
578                },
579                Object::Triple {
580                    subject: _subject2,
581                    predicate: _predicate2,
582                    object: _object2,
583                },
584            ) => todo!(),
585        }
586    }
587}