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