1use std::{borrow::Cow, cmp::Ordering, fmt::Display, sync::Arc};
2
3use bevy_ecs::prelude::*;
4use iri_s::IriS;
5use prefixmap::{error::PrefixMapError, PrefixMap};
6use rudof_rdf::rdf_core::{
7 term::{
8 self as r_term, literal::Literal, BlankNode, Iri, IriOrBlankNode, Term as Rudof_Term,
9 TermKind,
10 },
11 FocusRDF, Matcher, NeighsRDF, RDFError, SHACLPath,
12};
13use shacl_ir::compiled::schema_ir::SchemaIR as ShaclSchemaIR;
14use sophia_api::term::Term;
15
16use crate::prelude::*;
17
18#[derive(Component)]
19pub struct ShaclShapes {
20 shacl: ShaclSchemaIR,
21}
22impl ShaclShapes {
23 pub fn new(shacl: ShaclSchemaIR) -> Self {
24 Self { shacl }
25 }
26
27 pub fn ir(&self) -> &ShaclSchemaIR {
28 &self.shacl
29 }
30}
31unsafe impl Send for ShaclShapes {}
32
33#[derive(Clone, Debug)]
34pub struct Rdf {
35 focus: Option<MyTerm<'static>>,
36 triples: Arc<Vec<MyQuad<'static>>>,
37}
38impl Rdf {
39 pub fn new(triples: &Triples) -> Self {
40 Self {
41 focus: None,
42 triples: triples.0.clone(),
43 }
44 }
45}
46
47impl FocusRDF for Rdf {
48 fn set_focus(&mut self, focus: &Self::Term) {
49 self.focus = Some(focus.clone());
50 }
51
52 fn get_focus(&self) -> Option<&Self::Term> {
53 self.focus.as_ref()
54 }
55}
56
57impl NeighsRDF for Rdf {
58 fn triples(&self) -> std::result::Result<impl Iterator<Item = Self::Triple>, Self::Err> {
59 Ok(self.triples.iter().cloned())
60 }
61
62 fn triples_matching<S, P, O>(
63 &self,
64 subject: &S,
65 predicate: &P,
66 object: &O,
67 ) -> Result<impl Iterator<Item = Self::Triple> + '_, Self::Err>
68 where
69 S: Matcher<Self::Subject>,
70 P: Matcher<Self::IRI>,
71 O: Matcher<Self::Term>,
72 {
73 let subject: Option<MyTerm<'static>> = subject.value().cloned();
74 let predicate: Option<MyTerm<'static>> = predicate.value().cloned();
75 let object: Option<MyTerm<'static>> = object.value().cloned();
76 let triples = self.triples()?.filter_map(move |triple| {
77 let subject_matches = subject
78 .as_ref()
79 .map(|s| s == &triple.subject)
80 .unwrap_or(true);
81
82 let predicate_matches = predicate
83 .as_ref()
84 .map(|s| s == &triple.predicate)
85 .unwrap_or(true);
86
87 let object_matches = object.as_ref().map(|s| s == &triple.object).unwrap_or(true);
88
89 if subject_matches && predicate_matches && object_matches {
90 Some(triple)
91 } else {
92 None
93 }
94 });
95 Ok(triples)
96 }
97}
98
99impl<'b> TryInto<oxrdf::Term> for MyTerm<'static> {
100 type Error = ();
101
102 fn try_into(self) -> std::result::Result<oxrdf::Term, Self::Error> {
103 use oxigraph::{model as M, model::Term as T};
104 let output = match &self.ty {
105 Some(sophia_api::prelude::TermKind::Iri) => {
106 T::NamedNode(M::NamedNode::new(self.as_str()).map_err(|_| ())?)
107 }
108 Some(sophia_api::prelude::TermKind::Literal) => {
109 if let Some(dt) = sophia_api::prelude::Term::datatype(&self) {
110 let dt = M::NamedNode::new(dt.as_str()).map_err(|_| ())?;
111 T::Literal(M::Literal::new_typed_literal(self.value.as_ref(), dt))
112 } else if let Some(lang) = self.language_tag() {
113 T::Literal(
114 M::Literal::new_language_tagged_literal(self.value.as_ref(), lang.as_str())
115 .map_err(|_| ())?,
116 )
117 } else {
118 T::Literal(M::Literal::new_simple_literal(self.value.as_ref()))
119 }
120 }
121 Some(sophia_api::prelude::TermKind::BlankNode) => {
122 T::BlankNode(M::BlankNode::new(self.value.as_ref()).map_err(|_| ())?)
123 }
124 _ => {
125 return Err(());
126 }
127 };
128 return Ok(output);
129 }
130}
131
132impl TryFrom<MyTerm<'static>> for IriOrBlankNode {
133 type Error = ();
134
135 fn try_from(term: MyTerm<'static>) -> std::result::Result<Self, Self::Error> {
136 let MyTerm { ty, value, .. } = term;
137 if let Some(t) = ty {
138 match t {
139 sophia_api::term::TermKind::Iri => {
140 return Ok(IriOrBlankNode::Iri(IriS::new_unchecked(value.as_ref())))
141 }
142 sophia_api::term::TermKind::BlankNode => {
143 return Ok(IriOrBlankNode::BlankNode(value.to_string()))
144 }
145 _ => {}
146 }
147 }
148 Err(())
149 }
150}
151
152impl From<IriS> for MyTerm<'static> {
153 fn from(value: IriS) -> Self {
154 MyTerm::named_node(value.as_str(), 0..0).to_owned()
155 }
156}
157
158impl From<IriOrBlankNode> for MyTerm<'static> {
159 fn from(value: IriOrBlankNode) -> Self {
160 match value {
161 IriOrBlankNode::BlankNode(id) => MyTerm::blank_node(id, 0..0),
162 IriOrBlankNode::Iri(iri_s) => MyTerm::named_node(iri_s.as_str(), 0..0).to_owned(),
163 }
164 }
165}
166
167impl From<MyTerm<'static>> for IriS {
168 fn from(value: MyTerm<'static>) -> Self {
169 IriS::new_unchecked(value.as_str())
170 }
171}
172
173impl Iri for MyTerm<'static> {
174 fn as_str(&self) -> &str {
175 self.as_str()
176 }
177}
178
179impl From<MyQuad<'static>> for MyTerm<'static> {
180 fn from(value: MyQuad<'static>) -> Self {
181 tracing::error!(
182 "From<MyQuad<'static>> for MyTerm<'static> is not supported, using subject"
183 );
184 value.subject
185 }
186}
187
188impl Rudof_Term for MyTerm<'static> {
189 fn kind(&self) -> TermKind {
190 if let Some(ty) = self.ty {
191 map(ty)
192 } else {
193 TermKind::BlankNode
194 }
195 }
196
197 fn lexical_form(&self) -> String {
198 self.value.to_string()
199 }
200}
201
202impl BlankNode for MyTerm<'static> {
203 fn new(id: impl Into<String>) -> Self {
204 MyTerm::blank_node(id.into(), 0..0)
205 }
206
207 fn id(&self) -> &str {
208 &self.value
209 }
210}
211
212impl Literal for MyTerm<'static> {
213 fn lexical_form(&self) -> &str {
214 self.value.as_ref()
215 }
216
217 fn lang(&self) -> Option<r_term::literal::Lang> {
218 match &self.context {
219 TermContext::LangTag(cow) => return r_term::literal::Lang::new(cow.as_ref()).ok(),
220 _ => {}
221 }
222 None
223 }
224
225 fn datatype(&self) -> prefixmap::IriRef {
226 let str = match &self.context {
227 TermContext::DataType(cow) => cow.as_ref(),
228 _ => "http://www.w3.org/2001/XMLSchema#string",
229 };
230
231 prefixmap::IriRef::Iri(IriS::new_unchecked(str))
232 }
233
234 fn to_concrete_literal(&self) -> Option<r_term::literal::ConcreteLiteral> {
235 if self.ty == Some(sophia_api::term::TermKind::Literal) {
236 Some(self.clone().into())
237 } else {
238 None
239 }
240 }
241}
242
243impl From<r_term::Object> for MyTerm<'static> {
244 fn from(value: r_term::Object) -> Self {
245 use r_term::Object;
246 match value {
247 Object::Iri(iri_s) => MyTerm::named_node(iri_s.as_str(), 0..0).to_owned(),
248 Object::BlankNode(b) => MyTerm::blank_node(b, 0..0),
249 Object::Literal(sliteral) => sliteral.into(),
250 Object::Triple { subject, .. } => (*subject).into(),
251 }
252 }
253}
254
255fn dt_to_slit(value: &str, dt: &str) -> r_term::literal::ConcreteLiteral {
256 use r_term::literal::ConcreteLiteral as SLiteral;
257 if "http://www.w3.org/2001/XMLSchema#boolean" == dt {
258 if let Ok(b) = SLiteral::parse_bool(value) {
259 return SLiteral::BooleanLiteral(b);
260 }
261 }
262
263 if "http://www.w3.org/2001/XMLSchema#integer" == dt {
264 if let Ok(b) = SLiteral::parse_integer(value) {
265 return SLiteral::NumericLiteral(r_term::literal::NumericLiteral::Integer(b));
266 }
267 }
268
269 if "http://www.w3.org/2001/XMLSchema#double" == dt {
270 if let Ok(b) = SLiteral::parse_double(value) {
271 return SLiteral::NumericLiteral(r_term::literal::NumericLiteral::Double(b));
272 }
273 }
274
275 if "http://www.w3.org/2001/XMLSchema#decimal" == dt {
276 if let Ok(b) = SLiteral::parse_decimal(value) {
277 return SLiteral::NumericLiteral(r_term::literal::NumericLiteral::Decimal(b));
278 }
279 }
280
281 SLiteral::DatatypeLiteral {
282 lexical_form: value.to_string(),
283 datatype: prefixmap::IriRef::Iri(IriS::new_unchecked(&dt)),
284 }
285}
286
287impl From<MyTerm<'static>> for r_term::Object {
288 fn from(term: MyTerm<'static>) -> Self {
289 let MyTerm { ty, value, .. } = &term;
290 match ty.unwrap_or(sophia_api::prelude::TermKind::BlankNode) {
291 sophia_api::term::TermKind::Iri => {
292 r_term::Object::Iri(IriS::new_unchecked(value.as_ref()))
293 }
294 sophia_api::term::TermKind::BlankNode => r_term::Object::BlankNode(value.to_string()),
295 sophia_api::term::TermKind::Literal => r_term::Object::Literal(term.into()),
296 _ => {
297 tracing::error!(
298 "Unsupported into srdf::Object for MyTerm with type {:?}",
299 ty
300 );
301
302 r_term::Object::Iri(IriS::default())
303 }
304 }
305 }
306}
307
308impl From<r_term::literal::ConcreteLiteral> for MyTerm<'static> {
309 fn from(value: r_term::literal::ConcreteLiteral) -> Self {
310 let context = match value.lang() {
311 Some(dt) => TermContext::LangTag(Cow::Owned(dt.to_string())),
312 None => TermContext::DataType(Cow::Owned(value.datatype().to_string())),
313 };
314 MyTerm::literal(value.lexical_form(), 0..0, context)
315 }
316}
317
318impl From<bool> for MyTerm<'static> {
319 fn from(value: bool) -> Self {
320 MyTerm::literal(
321 value.to_string(),
322 0..0,
323 TermContext::DataType(Cow::Borrowed("http://www.w3.org/2001/XMLSchema#boolean")),
324 )
325 }
326}
327
328impl From<String> for MyTerm<'static> {
329 fn from(value: String) -> Self {
330 MyTerm::literal(
331 value,
332 0..0,
333 TermContext::DataType(Cow::Borrowed("http://www.w3.org/2001/XMLSchema#string")),
334 )
335 }
336}
337
338impl From<f64> for MyTerm<'static> {
339 fn from(value: f64) -> Self {
340 MyTerm::literal(
341 value.to_string(),
342 0..0,
343 TermContext::DataType(Cow::Borrowed("http://www.w3.org/2001/XMLSchema#double")),
344 )
345 }
346}
347
348impl From<i128> for MyTerm<'static> {
349 fn from(value: i128) -> Self {
350 MyTerm::literal(
351 value.to_string(),
352 0..0,
353 TermContext::DataType(Cow::Borrowed("http://www.w3.org/2001/XMLSchema#integer")),
354 )
355 }
356}
357
358impl From<MyTerm<'static>> for r_term::literal::ConcreteLiteral {
359 fn from(t: MyTerm<'static>) -> Self {
360 let value = t.value;
361 let context = t.context;
362 match context {
363 TermContext::None => r_term::literal::ConcreteLiteral::StringLiteral {
364 lexical_form: value.to_string(),
365 lang: None,
366 },
367 TermContext::DataType(dt) => dt_to_slit(value.as_ref(), dt.as_ref()),
368 TermContext::LangTag(cow) => r_term::literal::ConcreteLiteral::StringLiteral {
369 lexical_form: value.to_string(),
370 lang: r_term::literal::Lang::new(cow).ok(),
371 },
372 }
373 }
374}
375impl r_term::Triple<MyTerm<'static>, MyTerm<'static>, MyTerm<'static>> for MyQuad<'static> {
376 fn new(
377 subject: impl Into<MyTerm<'static>>,
378 predicate: impl Into<MyTerm<'static>>,
379 object: impl Into<MyTerm<'static>>,
380 ) -> Self {
381 MyQuad {
382 subject: subject.into(),
383 predicate: predicate.into(),
384 object: object.into(),
385 span: 0..0,
386 }
387 }
388
389 fn subj(&self) -> &MyTerm<'static> {
390 &self.subject
391 }
392
393 fn pred(&self) -> &MyTerm<'static> {
394 &self.predicate
395 }
396
397 fn obj(&self) -> &MyTerm<'static> {
398 &self.object
399 }
400
401 fn into_components(self) -> (MyTerm<'static>, MyTerm<'static>, MyTerm<'static>) {
402 let MyQuad {
403 subject,
404 predicate,
405 object,
406 ..
407 } = self;
408 (subject, predicate, object)
409 }
410}
411
412impl rudof_rdf::rdf_core::Rdf for Rdf {
419 type Subject = MyTerm<'static>;
420
421 type IRI = MyTerm<'static>;
422
423 type Term = MyTerm<'static>;
424
425 type BNode = MyTerm<'static>;
426
427 type Literal = MyTerm<'static>;
428
429 type Triple = MyQuad<'static>;
430
431 type Err = &'static str;
432
433 fn qualify_iri(&self, iri: &Self::IRI) -> String {
434 iri.value.to_string()
435 }
436
437 fn qualify_subject(&self, subj: &Self::Subject) -> String {
438 subj.value.to_string()
439 }
440
441 fn qualify_term(&self, term: &Self::Term) -> String {
442 term.value.to_string()
443 }
444
445 fn prefixmap(&self) -> Option<PrefixMap> {
446 None
447 }
448
449 fn resolve_prefix_local(
450 &self,
451 prefix: &str,
452 _local: &str,
453 ) -> std::result::Result<IriS, PrefixMapError> {
454 Err(PrefixMapError::PrefixNotFound {
455 prefix: prefix.to_string(),
456 prefixmap: PrefixMap::new(),
457 })
458 }
459
460 fn term_as_literal(term: &Self::Term) -> Result<Self::Literal, RDFError> {
461 if term.ty == Some(sophia_api::term::TermKind::Literal) {
462 <Self::Term as TryInto<Self::Literal>>::try_into(term.clone()).map_err(|_| {
463 RDFError::TermAsLiteral {
464 term: term.to_string(),
465 }
466 })
467 } else {
468 Err(RDFError::TermAsLiteral {
469 term: term.to_string(),
470 })
471 }
472 }
473
474 fn term_as_sliteral(term: &Self::Term) -> Result<r_term::literal::ConcreteLiteral, RDFError> {
475 let lit = Self::term_as_literal(term)?;
476 let slit =
477 <Self::Literal as TryInto<r_term::literal::ConcreteLiteral>>::try_into(lit.clone())
478 .map_err(|_| RDFError::LiteralAsSLiteral {
479 literal: lit.to_string(),
480 })?;
481 Ok(slit)
482 }
483
484 fn term_as_subject(term: &Self::Term) -> Result<Self::Subject, RDFError> {
485 if !(term.ty == Some(sophia_api::term::TermKind::BlankNode)
486 || term.ty == Some(sophia_api::term::TermKind::Iri))
487 {
488 return Err(RDFError::TermAsSubject {
489 term: term.to_string(),
490 });
491 }
492 <Self::Term as TryInto<Self::Subject>>::try_into(term.clone()).map_err(|_e| {
493 RDFError::TermAsSubject {
494 term: term.to_string(),
495 }
496 })
497 }
498
499 fn term_as_iri(term: &Self::Term) -> Result<Self::IRI, RDFError> {
502 if term.ty == Some(sophia_api::term::TermKind::Iri) {
503 <Self::Term as TryInto<Self::IRI>>::try_into(term.clone()).map_err(|_| {
504 RDFError::TermAsIri {
505 term: term.to_string(),
506 }
507 })
508 } else {
509 Err(RDFError::TermAsIri {
510 term: term.to_string(),
511 })
512 }
513 }
514
515 fn term_as_iris(term: &Self::Term) -> Result<IriS, RDFError> {
516 if term.ty != Some(sophia_api::term::TermKind::Iri) {
517 return Err(RDFError::TermAsIriS {
518 term: term.to_string(),
519 });
520 }
521
522 let iri = <Self::Term as TryInto<Self::IRI>>::try_into(term.clone()).map_err(|_| {
523 RDFError::TermAsIriS {
524 term: term.to_string(),
525 }
526 })?;
527 let iri_s: IriS = iri.into();
528 Ok(iri_s)
529 }
530
531 fn term_as_bnode(term: &Self::Term) -> Result<Self::BNode, RDFError> {
532 if term.ty != Some(sophia_api::term::TermKind::BlankNode) {
533 return Err(RDFError::TermAsBNode {
534 term: term.to_string(),
535 });
536 }
537 <Self::Term as TryInto<Self::BNode>>::try_into(term.clone()).map_err(|_| {
538 RDFError::TermAsBNode {
539 term: term.to_string(),
540 }
541 })
542 }
543
544 fn compare(&self, term1: &Self::Term, term2: &Self::Term) -> Result<Ordering, RDFError> {
545 term1
546 .partial_cmp(&term2)
547 .ok_or_else(|| RDFError::ComparisonError {
548 term1: term1.to_string(),
549 term2: term2.to_string(),
550 })
551 }
552}
553
554fn map(term: sophia_api::term::TermKind) -> r_term::TermKind {
555 match term {
556 sophia_api::term::TermKind::Iri => TermKind::Iri,
557 sophia_api::term::TermKind::Literal => TermKind::Literal,
558 sophia_api::term::TermKind::BlankNode => TermKind::BlankNode,
559 sophia_api::term::TermKind::Triple => TermKind::Triple,
560 sophia_api::term::TermKind::Variable => TermKind::BlankNode,
561 }
562}
563
564impl r_term::Subject for MyTerm<'_> {
565 fn kind(&self) -> r_term::TermKind {
566 if let Some(ty) = self.ty {
567 map(ty)
568 } else {
569 TermKind::BlankNode
570 }
571 }
572}
573impl Matcher<MyTerm<'static>> for MyTerm<'static> {
574 fn value(&self) -> Option<&MyTerm<'static>> {
575 Some(self)
576 }
577}
578
579pub struct PrefixedPath<'a> {
580 pub path: &'a SHACLPath,
581 pub prefixes: &'a Prefixes,
582}
583impl<'a> PrefixedPath<'a> {
584 pub fn new(path: &'a SHACLPath, prefixes: &'a Prefixes) -> Self {
585 Self { path, prefixes }
586 }
587}
588
589impl<'a> Display for PrefixedPath<'a> {
590 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
591 match self.path {
592 SHACLPath::Predicate { pred } => {
593 if let Some(shorten) = self.prefixes.shorten(pred.as_str()) {
594 write!(f, "{shorten}")
595 } else {
596 write!(f, "{pred}")
597 }
598 }
599 SHACLPath::Alternative { paths } => {
600 write!(f, "(")?;
601 let mut first = true;
602 for p in paths {
603 if !first {
604 write!(f, " | ")?;
605 }
606 write!(f, "{}", Self::new(p, self.prefixes))?;
607 first = false;
608 }
609 write!(f, ")")
610 }
611 SHACLPath::Sequence { paths } => {
612 write!(f, "(")?;
613 let mut first = true;
614 for p in paths {
615 if !first {
616 write!(f, " / ")?;
617 }
618 write!(f, "{}", Self::new(p, self.prefixes))?;
619 first = false;
620 }
621 write!(f, ")")
622 }
623 SHACLPath::Inverse { path } => {
624 write!(f, "^({})", Self::new(path, self.prefixes))
625 }
626 SHACLPath::ZeroOrMore { path } => {
627 write!(f, "({})*", Self::new(path, self.prefixes))
628 }
629 SHACLPath::OneOrMore { path } => {
630 write!(f, "({})+", Self::new(path, self.prefixes))
631 }
632 SHACLPath::ZeroOrOne { path } => {
633 write!(f, "({})?", Self::new(path, self.prefixes))
634 }
635 }
636 }
637}