1use std::{
2 borrow::{Borrow, Cow},
3 hash::Hash,
4 sync::Arc,
5 usize,
6};
7
8use bevy_ecs::prelude::*;
9use derive_more::{AsMut, AsRef, Deref, DerefMut};
10use sophia_api::{
11 prelude::{Any, Dataset},
12 quad::Quad,
13 term::{matcher::TermMatcher, BnodeId, GraphName, IriRef, Term, TermKind, VarName},
14 MownStr,
15};
16use tracing::{debug, instrument};
17
18use crate::{
19 components::{PositionComponent, RopeC},
20 prelude::DynLang,
21 util::{
22 ns::{owl, rdfs},
23 position_to_offset,
24 },
25};
26
27#[derive(Debug, PartialEq)]
30pub enum TripleTarget {
31 Subject,
32 Predicate,
33 Object,
34 Graph,
35}
36
37#[derive(Component, Debug)]
39pub struct TripleComponent {
40 pub triple: MyQuad<'static>,
41 pub target: TripleTarget,
42}
43
44impl TripleComponent {
45 pub fn kind(&self) -> TermKind {
46 let target = match self.target {
47 TripleTarget::Subject => self.triple.s().kind(),
48 TripleTarget::Predicate => self.triple.p().kind(),
49 TripleTarget::Object => self.triple.o().kind(),
50 TripleTarget::Graph => self
51 .triple
52 .g()
53 .map(|x| x.kind())
54 .unwrap_or(sophia_api::term::TermKind::Triple),
55 };
56 target
57 }
58
59 pub fn term(&self) -> Option<&MyTerm<'static>> {
60 let target = match self.target {
61 TripleTarget::Subject => self.triple.s(),
62 TripleTarget::Predicate => self.triple.p(),
63 TripleTarget::Object => self.triple.o(),
64 TripleTarget::Graph => return None,
65 };
66 Some(target)
67 }
68}
69
70#[derive(Component, AsRef, Deref, AsMut, DerefMut, Debug)]
74pub struct Triples(pub Arc<Vec<MyQuad<'static>>>);
75
76impl Triples {
77 pub fn object<'s, S, P>(&'s self, subj: S, pred: P) -> Option<&'s MyTerm<'s>>
78 where
79 S: TermMatcher + 's,
80 P: TermMatcher + 's,
81 {
82 self.0
83 .quads_matching(
84 subj,
85 pred,
86 sophia_api::prelude::Any,
87 sophia_api::prelude::Any,
88 )
89 .flatten()
90 .next()
91 .map(|x| x.o())
92 }
93
94 pub fn objects<'s, S, P>(&'s self, subj: S, pred: P) -> impl Iterator<Item = &'s MyTerm<'s>>
95 where
96 S: TermMatcher + 's,
97 P: TermMatcher + 's,
98 {
99 self.0
100 .quads_matching(
101 subj,
102 pred,
103 sophia_api::prelude::Any,
104 sophia_api::prelude::Any,
105 )
106 .flatten()
107 .map(|x| x.o())
108 }
109}
110
111#[instrument(skip(query, commands))]
112pub fn get_current_triple(
113 query: Query<
114 (Entity, &PositionComponent, &Triples, &RopeC, &DynLang),
115 Changed<PositionComponent>,
116 >,
117 mut commands: Commands,
118) {
119 for (e, position, triples, rope, lang) in &query {
120 commands.entity(e).remove::<TripleComponent>();
121
122 let Some(offset) = position_to_offset(position.0, &rope.0) else {
123 debug!("Couldn't transform to an offset");
124 continue;
125 };
126
127 if let Some((target, _, t)) = triples
132 .iter()
133 .flat_map(|t| {
134 [
135 (TripleTarget::Subject, &t.subject.span, t),
136 (TripleTarget::Predicate, &t.predicate.span, t),
137 (TripleTarget::Object, &t.object.span, t),
138 ]
139 })
140 .into_iter()
141 .filter(|x| x.1.contains(&offset))
142 .min_by_key(|x| x.1.end - x.1.start)
143 {
144 tracing::debug!(
145 "Narrowest triple at {} Component {:?} {} {:?} {} {:?} {} {:?} .",
146 offset,
147 target,
148 t.subject,
149 t.subject.span,
150 t.predicate,
151 t.predicate.span,
152 t.object,
153 t.object.span
154 );
155 commands.entity(e).insert(TripleComponent {
156 triple: t.clone(),
157 target,
158 });
159 } else {
160 let found = triples
161 .0
162 .iter()
163 .filter(|triple| triple.span.start <= offset && offset <= triple.span.end)
164 .min_by_key(|x| x.span.end - x.span.start);
165
166 if let Some(t) = found.or_else(|| {
171 triples
172 .0
173 .iter()
174 .filter(|triple| triple.span.end <= offset)
175 .max_by_key(|x| x.span.end)
176 }) {
177 let object_written = t.object.span.start < t.object.span.end;
178 let predicate_written = t.predicate.span.start < t.predicate.span.end;
179 let target = if object_written && offset > t.object.span.end {
180 TripleTarget::Predicate
182 } else if predicate_written && offset >= t.predicate.span.end {
183 TripleTarget::Object
184 } else if offset >= t.subject.span.end {
185 TripleTarget::Predicate
186 } else {
187 lang.default_position()
188 };
189 tracing::debug!(
190 "Triple at {} Component {:?} {} {:?} {} {:?} {} {:?} .",
191 offset,
192 target,
193 t.subject,
194 t.subject.span,
195 t.predicate,
196 t.predicate.span,
197 t.object,
198 t.object.span
199 );
200 commands.entity(e).insert(TripleComponent {
201 triple: t.clone(),
202 target,
203 });
204 } else {
205 commands.entity(e).remove::<TripleComponent>();
206 debug!("No current triple found");
207 }
208 }
209 }
210}
211
212#[derive(Debug, Clone)]
213pub struct MyQuad<'a> {
214 pub subject: MyTerm<'a>,
215 pub predicate: MyTerm<'a>,
216 pub object: MyTerm<'a>,
217 pub span: std::ops::Range<usize>,
218}
219impl<'b, 'a> TryFrom<&'b MyQuad<'a>> for oxigraph::model::Quad {
220 type Error = ();
221
222 fn try_from(value: &'b MyQuad<'a>) -> std::result::Result<Self, Self::Error> {
223 let subject = oxigraph::model::Term::try_from(&value.subject)?;
224 let predicate = oxigraph::model::Term::try_from(&value.predicate)?;
225 let object = oxigraph::model::Term::try_from(&value.object)?;
226
227 let subject = oxigraph::model::NamedOrBlankNode::try_from(subject).map_err(|_| ())?;
228 let predicate = oxigraph::model::NamedNode::try_from(predicate).map_err(|_| ())?;
229
230 Ok(oxigraph::model::Quad::new(
231 subject,
232 predicate,
233 object,
234 oxigraph::model::GraphName::default(),
235 ))
236 }
237}
238
239impl<'a> MyQuad<'a> {
240 pub fn into_oxi_graph(
241 &self,
242 graph: impl TryInto<oxigraph::model::Term>,
243 ) -> Result<oxigraph::model::Quad, ()> {
244 let graph = graph.try_into().map_err(|_| ())?;
245 let graph = oxigraph::model::NamedOrBlankNode::try_from(graph).map_err(|_| ())?;
246 let graph = oxigraph::model::GraphName::from(graph);
247
248 self.into_oxi(Some(graph))
249 }
250
251 pub fn into_oxi(
252 &self,
253 graph: Option<oxigraph::model::GraphName>,
254 ) -> Result<oxigraph::model::Quad, ()> {
255 let subject = oxigraph::model::Term::try_from(&self.subject)?;
256 let predicate = oxigraph::model::Term::try_from(&self.predicate)?;
257 let object = oxigraph::model::Term::try_from(&self.object)?;
258 let graph = graph.unwrap_or_default();
259
260 let subject = oxigraph::model::NamedOrBlankNode::try_from(subject).map_err(|_| ())?;
261 let predicate = oxigraph::model::NamedNode::try_from(predicate).map_err(|_| ())?;
262
263 Ok(oxigraph::model::Quad::new(
264 subject, predicate, object, graph,
265 ))
266 }
267}
268
269impl<'a> std::fmt::Display for MyQuad<'a> {
270 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
271 write!(
272 f,
273 "{} {} {}. # {:?}",
274 self.subject, self.predicate, self.object, self.span
275 )
276 }
277}
278
279impl<'a> MyQuad<'a> {
280 pub fn to_owned(&self) -> MyQuad<'static> {
281 MyQuad {
282 subject: self.subject.to_owned(),
283 predicate: self.predicate.to_owned(),
284 object: self.object.to_owned(),
285 span: self.span.clone(),
286 }
287 }
288}
289impl<'a, 'b> TryFrom<&'b MyTerm<'a>> for oxigraph::model::Term {
290 type Error = ();
291
292 fn try_from(value: &'b MyTerm<'a>) -> std::result::Result<Self, Self::Error> {
293 use oxigraph::{model as M, model::Term as T};
294 let output = match &value.ty {
295 Some(TermKind::Iri) => T::NamedNode(M::NamedNode::new(value.as_str()).map_err(|_| ())?),
296 Some(TermKind::Literal) => {
297 if let Some(dt) = value.datatype() {
298 let dt = M::NamedNode::new(dt.as_str()).map_err(|_| ())?;
299 T::Literal(M::Literal::new_typed_literal(value.value.as_ref(), dt))
300 } else if let Some(lang) = value.language_tag() {
301 T::Literal(
302 M::Literal::new_language_tagged_literal(
303 value.value.as_ref(),
304 lang.as_str(),
305 )
306 .map_err(|_| ())?,
307 )
308 } else {
309 T::Literal(M::Literal::new_simple_literal(value.value.as_ref()))
310 }
311 }
312 Some(TermKind::BlankNode) => {
313 T::BlankNode(M::BlankNode::new(value.value.as_ref()).map_err(|_| ())?)
314 }
315 _ => {
316 return Err(());
317 }
318 };
319 return Ok(output);
320 }
321}
322
323impl<'a> Quad for MyQuad<'a> {
324 type Term = MyTerm<'a>;
325
326 fn s(&self) -> sophia_api::quad::QBorrowTerm<'_, Self> {
327 self.subject.borrow_term()
328 }
329
330 fn p(&self) -> sophia_api::quad::QBorrowTerm<'_, Self> {
331 self.predicate.borrow_term()
332 }
333
334 fn o(&self) -> sophia_api::quad::QBorrowTerm<'_, Self> {
335 self.object.borrow_term()
336 }
337
338 fn g(&self) -> GraphName<sophia_api::quad::QBorrowTerm<'_, Self>> {
339 None
340 }
341
342 fn to_spog(self) -> sophia_api::quad::Spog<Self::Term> {
343 ([self.subject, self.predicate, self.object], None)
344 }
345}
346#[derive(Debug, Clone, PartialEq, Hash, Eq)]
349pub enum TermContext<'a> {
350 None,
351 DataType(Cow<'a, str>),
352 LangTag(Cow<'a, str>),
353}
354impl<'a> TermContext<'a> {
355 pub fn to_owned(&self) -> TermContext<'static> {
356 match self {
357 TermContext::None => TermContext::None,
358 TermContext::DataType(cow) => TermContext::DataType(Cow::Owned(cow.to_string())),
359 TermContext::LangTag(cow) => TermContext::LangTag(Cow::Owned(cow.to_string())),
360 }
361 }
362}
363
364#[derive(Debug, Clone, Eq)]
365pub struct MyTerm<'a> {
366 pub value: Cow<'a, str>,
367 pub ty: Option<TermKind>,
368 pub span: std::ops::Range<usize>,
369 pub context: TermContext<'a>,
370}
371impl<'a> PartialOrd for MyTerm<'a> {
372 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
373 match self.value.partial_cmp(&other.value) {
374 Some(core::cmp::Ordering::Equal) => {}
375 ord => return ord,
376 }
377 self.ty.partial_cmp(&other.ty)
378 }
379}
380impl<'a> Ord for MyTerm<'a> {
381 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
382 match self.value.cmp(&other.value) {
383 core::cmp::Ordering::Equal => {}
384 ord => return ord,
385 }
386 self.ty.cmp(&other.ty)
387 }
388}
389
390impl Hash for MyTerm<'_> {
391 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
392 self.value.hash(state);
394 self.ty.hash(state);
395 }
396}
397
398impl PartialEq for MyTerm<'_> {
399 fn eq(&self, other: &Self) -> bool {
400 other.value == self.value && other.ty == self.ty
402 }
403}
404
405impl<'a> std::fmt::Display for MyTerm<'a> {
406 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
407 match self.kind() {
408 TermKind::Iri => write!(f, "<{}>", self.value),
409 TermKind::Literal => write!(f, "\"{}\"", self.value),
410 TermKind::BlankNode => write!(f, "_:{}", self.value),
411 TermKind::Triple => write!(f, "<{}>", self.value),
412 TermKind::Variable => write!(f, "?{}", self.value),
413 }
414 }
415}
416
417impl<'a> MyTerm<'a> {
418 pub fn to_owned(&self) -> MyTerm<'static> {
419 let value = Cow::Owned(self.value.to_string());
420 MyTerm {
421 value,
422 ty: self.ty.clone(),
423 span: self.span.clone(),
424 context: self.context.to_owned(),
425 }
426 }
427 pub fn variable<T: Into<Cow<'a, str>>>(value: T, span: std::ops::Range<usize>) -> Self {
428 Self {
429 value: value.into(),
430 ty: TermKind::Variable.into(),
431 span,
432 context: TermContext::None,
433 }
434 }
435 pub fn named_node<T: Into<Cow<'a, str>>>(value: T, span: std::ops::Range<usize>) -> Self {
436 Self {
437 value: value.into(),
438 ty: TermKind::Iri.into(),
439 span,
440 context: TermContext::None,
441 }
442 }
443 pub fn blank_node<T: Into<Cow<'a, str>>>(value: T, span: std::ops::Range<usize>) -> Self {
444 Self {
445 value: value.into(),
446 ty: TermKind::BlankNode.into(),
447 span,
448 context: TermContext::None,
449 }
450 }
451 pub fn literal<T: Into<Cow<'a, str>>>(
452 value: T,
453 span: std::ops::Range<usize>,
454 context: TermContext<'a>,
455 ) -> Self {
456 Self {
457 value: value.into(),
458 ty: TermKind::Literal.into(),
459 span,
460 context,
461 }
462 }
463
464 pub fn invalid(span: std::ops::Range<usize>) -> Self {
465 Self {
466 value: Cow::default(),
467 ty: TermKind::Iri.into(),
468 span,
469
470 context: TermContext::None,
471 }
472 }
473
474 pub fn as_str(&'a self) -> &'a str {
475 &self.value
476 }
477}
478
479impl<'a> Term for MyTerm<'a> {
480 type BorrowTerm<'x>
481 = &'x Self
482 where
483 Self: 'x;
484
485 fn kind(&self) -> sophia_api::term::TermKind {
486 self.ty.unwrap_or(TermKind::Triple)
487 }
488
489 fn borrow_term(&self) -> Self::BorrowTerm<'_> {
490 self
491 }
492
493 fn iri(&self) -> Option<sophia_api::term::IriRef<sophia_api::MownStr<'_>>> {
494 self.is_iri()
495 .then(|| IriRef::new_unchecked(MownStr::from_ref(&self.value)))
496 }
497
498 fn bnode_id(&self) -> Option<sophia_api::term::BnodeId<sophia_api::MownStr<'_>>> {
499 self.is_blank_node()
500 .then(|| BnodeId::new_unchecked(MownStr::from_ref(&self.value)))
501 }
502
503 fn lexical_form(&self) -> Option<sophia_api::MownStr<'_>> {
504 self.is_literal().then(|| MownStr::from_ref(&self.value))
505 }
506
507 fn datatype(&self) -> Option<sophia_api::term::IriRef<sophia_api::MownStr<'_>>> {
508 None
509 }
510
511 fn language_tag(&self) -> Option<sophia_api::term::LanguageTag<sophia_api::MownStr<'_>>> {
512 None
513 }
514
515 fn variable(&self) -> Option<sophia_api::term::VarName<sophia_api::MownStr<'_>>> {
516 self.is_variable()
517 .then(|| VarName::new_unchecked(MownStr::from_ref(&self.value)))
518 }
519
520 fn triple(&self) -> Option<[Self::BorrowTerm<'_>; 3]> {
521 panic!("MyTerm does not supported triples")
522 }
523
524 fn to_triple(self) -> Option<[Self; 3]>
525 where
526 Self: Sized,
527 {
528 panic!("MyTerm does not supported triples")
529 }
530}
531
532impl<'a> Borrow<str> for &MyTerm<'a> {
533 fn borrow(&self) -> &str {
534 &self.value
535 }
536}
537
538#[derive(Default, Debug)]
539pub struct Triples2<'a> {
540 pub base_url: String,
541 pub triples: Vec<MyQuad<'a>>,
542 pub base: Option<MyTerm<'a>>,
543}
544
545impl<'a> Triples2<'a> {
546 pub fn to_owned(&self) -> Triples2<'static> {
547 let triples = self.triples.iter().map(|q| q.to_owned()).collect();
548 let base: Option<MyTerm<'static>> = self.base.as_ref().map(|x| x.to_owned());
549
550 Triples2 {
551 base,
552 triples,
553 base_url: self.base_url.clone(),
554 }
555 }
556
557 pub fn imports(&self, cb: impl FnMut(IriRef<MownStr<'_>>) -> ()) {
558 if let Some(ref base) = self.base {
559 self.triples
560 .quads_matching([base], [owl::imports], Any, Any)
561 .flatten()
562 .flat_map(|s| s.o().iri())
563 .for_each(cb);
564 }
565 }
566
567 pub fn sub_class_of(&self, mut cb: impl FnMut(IriRef<MownStr<'_>>, IriRef<MownStr<'_>>) -> ()) {
568 self.triples
569 .quads_matching(Any, [rdfs::subClassOf], Any, Any)
570 .flatten()
571 .flat_map(|s| match (s.s().iri(), s.o().iri()) {
572 (Some(s), Some(o)) => Some((s, o)),
573 _ => None,
574 })
575 .for_each(|(x, y)| cb(x, y));
576 }
577}
578
579impl<'a> std::ops::Deref for Triples2<'a> {
580 type Target = Vec<MyQuad<'a>>;
581
582 fn deref(&self) -> &Self::Target {
583 &self.triples
584 }
585}
586
587impl<'a> PartialEq for MyQuad<'a> {
588 fn eq(&self, other: &Self) -> bool {
589 self.subject == other.subject
590 && self.predicate == other.predicate
591 && self.object == other.object
592 }
593}