Skip to main content

rudof_rdf/rdf_core/
rdf.rs

1use crate::rdf_core::{
2    Matcher, RDFError,
3    term::{
4        BlankNode, Iri, IriOrBlankNode, Object, Subject, Term, Triple,
5        literal::{ConcreteLiteral, Lang, Literal},
6    },
7};
8use iri_s::IriS;
9use prefixmap::PrefixMap;
10use prefixmap::PrefixMapError;
11use rust_decimal::Decimal;
12use std::cmp::Ordering;
13use std::fmt::Display;
14
15/// A trait representing an RDF graph implementation with its associated types and operations.
16///
17/// This trait defines the core interface for working with RDF data structures, including
18/// subjects, predicates, objects, literals, and triples. It provides type-safe conversions
19/// between different RDF components and utility methods for common RDF operations.
20///
21/// # Associated Types
22///
23/// Implementors must define concrete types for all RDF components:
24/// - `Subject`: Resources that can appear as triple subjects
25/// - `IRI`: Internationalized Resource Identifiers
26/// - `Term`: Any RDF term (IRIs, literals, blank nodes, or triples)
27/// - `BNode`: Blank nodes (anonymous resources)
28/// - `Literal`: Literal values (strings, numbers, dates, etc.)
29/// - `Triple`: RDF statements (subject-predicate-object)
30/// - `Err`: Error type for operations that can fail
31pub trait Rdf: Sized {
32    /// The subject type for this RDF implementation.
33    type Subject: Subject
34        + From<Self::IRI>
35        + From<Self::BNode>
36        + From<IriS>
37        + From<IriOrBlankNode>
38        + TryFrom<Self::Term>
39        + TryInto<IriOrBlankNode>
40        + TryFrom<Object>
41        + Matcher<Self::Subject>;
42
43    /// The IRI type for this RDF implementation.
44    type IRI: Iri + From<IriS> + TryFrom<Self::Term> + Matcher<Self::IRI> + Into<IriS>;
45
46    /// The term type representing any RDF component.
47    type Term: Term
48        + From<Self::Subject>
49        + From<Self::IRI>
50        + From<Self::BNode>
51        + From<Self::Literal>
52        + From<Self::Triple>
53        + From<IriS>
54        + From<Object>
55        + TryInto<Object>
56        + Matcher<Self::Term>
57        + PartialEq;
58
59    /// The blank node type for this RDF implementation.
60    type BNode: BlankNode + TryFrom<Self::Term>;
61
62    /// The literal type for representing data values.
63    type Literal: Literal
64        + From<bool>
65        + From<String>
66        + From<i128>
67        + From<f64>
68        + TryFrom<Self::Term>
69        + From<ConcreteLiteral>
70        + TryInto<ConcreteLiteral>;
71
72    /// The triple type representing RDF statements.
73    type Triple: Triple<Self::Subject, Self::IRI, Self::Term>;
74
75    /// The error type for fallible operations.
76    type Err: Display;
77
78    /// Returns the prefixed name corresponding to an IRI.
79    ///
80    /// Converts a full IRI to its shortened form using registered namespace prefixes.
81    ///
82    /// # Parameters
83    ///
84    /// * `iri` - The IRI to qualify
85    fn qualify_iri(&self, iri: &Self::IRI) -> String;
86
87    /// Returns the prefixed representation of a subject.
88    ///
89    /// Converts a subject to its qualified string form, applying prefix mappings
90    /// if the subject is an IRI.
91    ///
92    /// # Parameters
93    ///
94    /// * `subj` - The subject to qualify
95    fn qualify_subject(&self, subj: &Self::Subject) -> String;
96
97    /// Returns the prefixed representation of a term.
98    ///
99    /// Converts a term to its qualified string form, applying prefix mappings
100    /// where applicable.
101    ///
102    /// # Parameters
103    ///
104    /// * `term` - The term to qualify
105    fn qualify_term(&self, term: &Self::Term) -> String;
106
107    /// Returns the prefix map used by this RDF implementation.
108    ///
109    /// Returns `None` if no prefix map is configured.
110    fn prefixmap(&self) -> Option<PrefixMap>;
111
112    /// Resolves a prefix and local name to obtain the full IRI.
113    ///
114    /// Combines a namespace prefix with a local name to produce the complete IRI
115    ///
116    /// # Parameters
117    ///
118    /// * `prefix` - The namespace prefix
119    /// * `local` - The local name
120    ///
121    /// # Errors
122    ///
123    /// Returns `PrefixMapError` if the prefix is not registered in the prefix map.
124    fn resolve_prefix_local(&self, prefix: &str, local: &str) -> Result<IriS, PrefixMapError>;
125
126    /// Extracts the numeric value from a term, if it represents a number.
127    ///
128    /// Attempts to convert the term to a literal and extract its numeric value
129    /// as a `Decimal`. Returns `None` if the term is not a numeric literal.
130    ///
131    /// # Parameters
132    ///
133    /// * `term` - The term to extract the numeric value from
134    fn numeric_value(&self, term: &Self::Term) -> Option<Decimal> {
135        let maybe_object: Result<Object, _> = term.clone().try_into();
136        match maybe_object {
137            Ok(object) => object.numeric_value().map(|n| n.to_decimal().unwrap()),
138            Err(_) => None,
139        }
140    }
141
142    /// Converts a term to a literal.
143    ///
144    /// # Parameters
145    ///
146    /// * `term` - The term to convert
147    ///
148    /// # Errors
149    ///
150    /// Returns `RDFError::TermAsLiteral` if the term is not a literal.
151    fn term_as_literal(term: &Self::Term) -> Result<Self::Literal, RDFError> {
152        Self::Literal::try_from(term.clone()).map_err(|_| RDFError::TermAsLiteral { term: term.to_string() })
153    }
154
155    /// Attempts to convert a term into a concrete literal.
156    ///
157    /// # Parameters
158    ///
159    /// * `term` - The term to convert
160    /// # Errors
161    ///
162    /// Returns `RDFError::TermAsLiteral` if the term cannot be converted into a literal.
163    /// Returns `RDFError::LiteralAsSLiteral` if the resulting literal cannot be converted into a concrete literal.
164    fn term_as_sliteral(term: &Self::Term) -> Result<ConcreteLiteral, RDFError> {
165        let lit = <Self::Term as TryInto<Self::Literal>>::try_into(term.clone())
166            .map_err(|_| RDFError::TermAsLiteral { term: term.to_string() })?;
167        let slit = <Self::Literal as TryInto<ConcreteLiteral>>::try_into(lit.clone()).map_err(|_| {
168            RDFError::LiteralAsSLiteral {
169                literal: lit.to_string(),
170            }
171        })?;
172        Ok(slit)
173    }
174
175    /// Converts a term to a subject.
176    ///
177    /// # Parameters
178    ///
179    /// * `term` - The term to convert
180    ///
181    /// # Errors
182    ///
183    /// Returns `RDFError::TermAsSubject` if the term cannot be used as a subject
184    fn term_as_subject(term: &Self::Term) -> Result<Self::Subject, RDFError> {
185        Self::Subject::try_from(term.clone()).map_err(|_| RDFError::TermAsSubject { term: term.to_string() })
186    }
187
188    /// Converts a subject to a term.
189    ///
190    /// # Parameters
191    ///
192    /// * `subj` - The subject to convert
193    fn subject_as_term(subj: &Self::Subject) -> Self::Term {
194        subj.clone().into()
195    }
196
197    /// Converts a triple to a term (RDF-star support).
198    ///
199    /// In RDF-star, triples can be used as terms in other triples.
200    ///
201    /// # Parameters
202    ///
203    /// * `triple` - The triple to convert
204    fn triple_as_term(triple: &Self::Triple) -> Self::Term {
205        Self::Term::from(triple.clone())
206    }
207
208    /// Converts an `IriS` to a term.
209    ///
210    /// # Parameters
211    ///
212    /// * `iri` - The IriS to convert
213    fn iris_as_term(iri: &IriS) -> Self::Term {
214        Self::Term::from(Self::IRI::from(iri.clone()))
215    }
216
217    /// Converts a term to an IRI.
218    ///
219    /// # Parameters
220    ///
221    /// * `term` - The term to convert
222    ///
223    /// # Errors
224    ///
225    /// Returns `RDFError::TermAsIri` if the term is not an IRI.
226    fn term_as_iri(term: &Self::Term) -> Result<Self::IRI, RDFError> {
227        Self::IRI::try_from(term.clone()).map_err(|_| RDFError::TermAsIri { term: term.to_string() })
228    }
229
230    /// Converts an IRI or blank node to a term.
231    ///
232    /// # Parameters
233    ///
234    /// * `ib` - The IRI or blank node to convert
235    fn iri_or_bnode_as_term(ib: &IriOrBlankNode) -> Self::Term {
236        let subject: Self::Subject = ib.clone().into();
237        subject.into()
238    }
239
240    /// Converts a term to a blank node.
241    ///
242    /// # Parameters
243    ///
244    /// * `term` - The term to convert
245    ///
246    /// # Errors
247    ///
248    /// Returns `RDFError::TermAsBNode` if the term is not a blank node.
249    fn term_as_bnode(term: &Self::Term) -> Result<Self::BNode, RDFError> {
250        Self::BNode::try_from(term.clone()).map_err(|_| RDFError::TermAsBNode { term: term.to_string() })
251    }
252
253    /// Converts a term to an `IriS`.
254    ///
255    /// # Parameters
256    ///
257    /// * `term` - The term to convert
258    ///
259    /// # Errors
260    ///
261    /// Returns `RDFError::TermAsIriS` if the term is not an IRI.
262    fn term_as_iris(term: &Self::Term) -> Result<IriS, RDFError> {
263        let iri = Self::IRI::try_from(term.clone()).map_err(|_| RDFError::TermAsIriS { term: term.to_string() })?;
264        let iri_s: IriS = iri.into();
265        Ok(iri_s)
266    }
267
268    /// Converts a term to an `Object`.
269    ///
270    /// # Parameters
271    ///
272    /// * `term` - The term to convert
273    ///
274    /// # Errors
275    ///
276    /// Returns `RDFError::TermAsObject` if the conversion fails.
277    fn term_as_object(term: &Self::Term) -> Result<Object, RDFError> {
278        <Self::Term as TryInto<Object>>::try_into(term.clone()).map_err(|_e| RDFError::TermAsObject {
279            term: format!("Converting term to object: {term}"),
280            error: "Error term_as_object".to_string(),
281        })
282    }
283
284    /// Converts an `Object` to a term.
285    ///
286    /// # Parameters
287    ///
288    /// * `object` - The object to convert
289    fn object_as_term(object: &Object) -> Self::Term {
290        Self::Term::from(object.clone())
291    }
292
293    /// Converts a subject to an `Object`.
294    ///
295    /// # Parameters
296    ///
297    /// * `subject` - The subject to convert
298    ///
299    /// # Errors
300    ///
301    /// Returns `RDFError` if the subject cannot be converted to an object.
302    fn subject_as_node(subject: &Self::Subject) -> Result<Object, RDFError> {
303        let term = Self::subject_as_term(subject);
304        let object = Self::term_as_object(&term)?;
305        Ok(object)
306    }
307
308    /// Extracts a language tag from a term.
309    ///
310    /// # Parameters
311    ///
312    /// * `term` - The term to extract the language tag from
313    ///
314    /// # Errors
315    ///
316    /// Returns `RDFError::TermAsLang` if the term is not a language-tagged literal.
317    fn term_as_lang(term: &Self::Term) -> std::result::Result<Lang, RDFError> {
318        if term.is_blank_node() {
319            Err(RDFError::TermAsLang { term: term.to_string() })
320        } else if let Ok(literal) = Self::term_as_literal(term) {
321            let lang = Lang::new(literal.lexical_form());
322            match lang {
323                Ok(lang) => Ok(lang),
324                Err(_) => todo!(),
325            }
326        } else {
327            todo!()
328        }
329    }
330
331    /// Compares two terms according to SPARQL ordering semantics.
332    ///
333    /// The comparison follows the SPARQL 1.1 specification for operator mapping:
334    /// <https://www.w3.org/TR/sparql11-query/#OperatorMapping>
335    ///
336    /// # Parameters
337    ///
338    /// * `term1` - The first term to compare
339    /// * `term2` - The second term to compare
340    ///
341    /// # Errors
342    ///
343    /// Returns `RDFError::ComparisonError` if the terms cannot be compared
344    fn compare(&self, term1: &Self::Term, term2: &Self::Term) -> Result<Ordering, RDFError> {
345        // TODO: At this moment we convert the terms to object and perform the comparison within objects
346        // This requires to clone but we should be able to optimize this later
347        let obj1: Object = Self::term_as_object(term1)?;
348        let obj2: Object = Self::term_as_object(term2)?;
349        obj1.partial_cmp(&obj2).ok_or_else(|| RDFError::ComparisonError {
350            term1: term1.lexical_form(),
351            term2: term2.lexical_form(),
352        })
353    }
354
355    /// Checks if two terms are equal according to SPARQL semantics.
356    ///
357    /// The equality follows the SPARQL 1.1 specification for operator mapping:
358    /// <https://www.w3.org/TR/sparql11-query/#OperatorMapping>
359    ///
360    /// # Parameters
361    ///
362    /// * `term1` - The first term
363    /// * `term2` - The second term
364    fn equals(&self, term1: &Self::Term, term2: &Self::Term) -> bool {
365        term1 == term2
366    }
367}