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}