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}