1use std::{borrow::Cow, hash::Hash, usize};
2
3use bevy_ecs::prelude::*;
4use derive_more::{AsMut, AsRef, Deref, DerefMut};
5use sophia_api::{
6 prelude::{Any, Dataset},
7 quad::Quad,
8 term::{matcher::TermMatcher, BnodeId, GraphName, IriRef, Term, TermKind},
9 MownStr,
10};
11use tracing::{debug, instrument};
12
13use crate::{
14 components::{PositionComponent, RopeC},
15 util::{
16 ns::{owl, rdfs},
17 position_to_offset,
18 },
19};
20
21#[derive(Debug, PartialEq)]
24pub enum TripleTarget {
25 Subject,
26 Predicate,
27 Object,
28 Graph,
29}
30
31#[derive(Component, Debug)]
33pub struct TripleComponent {
34 pub triple: MyQuad<'static>,
35 pub target: TripleTarget,
36}
37impl TripleComponent {
38 pub fn kind(&self) -> TermKind {
39 let target = match self.target {
40 TripleTarget::Subject => self.triple.s().kind(),
41 TripleTarget::Predicate => self.triple.p().kind(),
42 TripleTarget::Object => self.triple.o().kind(),
43 TripleTarget::Graph => self
44 .triple
45 .g()
46 .map(|x| x.kind())
47 .unwrap_or(sophia_api::term::TermKind::Triple),
48 };
49 target
50 }
51
52 pub fn term(&self) -> Option<&MyTerm<'static>> {
53 let target = match self.target {
54 TripleTarget::Subject => self.triple.s(),
55 TripleTarget::Predicate => self.triple.p(),
56 TripleTarget::Object => self.triple.o(),
57 TripleTarget::Graph => return None,
58 };
59 Some(target)
60 }
61}
62
63#[derive(Component, AsRef, Deref, AsMut, DerefMut, Debug)]
67pub struct Triples(pub Vec<MyQuad<'static>>);
68
69impl Triples {
70 pub fn object<'s, S, P>(&'s self, subj: S, pred: P) -> Option<&'s MyTerm<'s>>
71 where
72 S: TermMatcher + 's,
73 P: TermMatcher + 's,
74 {
75 self.0
76 .quads_matching(
77 subj,
78 pred,
79 sophia_api::prelude::Any,
80 sophia_api::prelude::Any,
81 )
82 .flatten()
83 .next()
84 .map(|x| x.o())
85 }
86
87 pub fn objects<'s, S, P>(&'s self, subj: S, pred: P) -> impl Iterator<Item = &'s MyTerm<'s>>
88 where
89 S: TermMatcher + 's,
90 P: TermMatcher + 's,
91 {
92 self.0
93 .quads_matching(
94 subj,
95 pred,
96 sophia_api::prelude::Any,
97 sophia_api::prelude::Any,
98 )
99 .flatten()
100 .map(|x| x.o())
101 }
102}
103
104#[instrument(skip(query, commands))]
105pub fn get_current_triple(
106 query: Query<(Entity, &PositionComponent, &Triples, &RopeC)>,
107 mut commands: Commands,
108) {
109 for (e, position, triples, rope) in &query {
110 commands.entity(e).remove::<TripleComponent>();
111
112 let Some(offset) = position_to_offset(position.0, &rope.0) else {
113 debug!("Couldn't transform to an offset");
114 continue;
115 };
116
117 if let Some(t) = triples
118 .0
119 .iter()
120 .filter(|triple| triple.span.contains(&offset))
121 .min_by_key(|x| x.span.end - x.span.start)
122 {
123 let target = [
124 (TripleTarget::Subject, &t.subject.span),
125 (TripleTarget::Predicate, &t.predicate.span),
126 (TripleTarget::Object, &t.object.span),
127 ]
128 .into_iter()
129 .filter(|x| x.1.contains(&offset))
130 .min_by_key(|x| x.1.end - x.1.start)
131 .map(|x| x.0)
132 .unwrap_or(TripleTarget::Subject);
133
134 debug!("Current triple {} {:?}", t, target);
135 commands.entity(e).insert(TripleComponent {
136 triple: t.clone(),
137 target,
138 });
139 } else {
140 debug!("No current triple found");
141 }
142 }
143}
144
145#[derive(Debug, Clone)]
146pub struct MyQuad<'a> {
147 pub subject: MyTerm<'a>,
148 pub predicate: MyTerm<'a>,
149 pub object: MyTerm<'a>,
150 pub span: std::ops::Range<usize>,
151}
152impl<'a> std::fmt::Display for MyQuad<'a> {
153 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154 write!(
155 f,
156 "{} {} {}. # {:?}",
157 self.subject, self.predicate, self.object, self.span
158 )
159 }
160}
161
162impl<'a> MyQuad<'a> {
163 pub fn to_owned(&self) -> MyQuad<'static> {
164 MyQuad {
165 subject: self.subject.to_owned(),
166 predicate: self.predicate.to_owned(),
167 object: self.object.to_owned(),
168 span: self.span.clone(),
169 }
170 }
171}
172
173impl<'a> Quad for MyQuad<'a> {
174 type Term = MyTerm<'a>;
175
176 fn s(&self) -> sophia_api::quad::QBorrowTerm<Self> {
177 self.subject.borrow_term()
178 }
179
180 fn p(&self) -> sophia_api::quad::QBorrowTerm<Self> {
181 self.predicate.borrow_term()
182 }
183
184 fn o(&self) -> sophia_api::quad::QBorrowTerm<Self> {
185 self.object.borrow_term()
186 }
187
188 fn g(&self) -> GraphName<sophia_api::quad::QBorrowTerm<Self>> {
189 None
190 }
191
192 fn to_spog(self) -> sophia_api::quad::Spog<Self::Term> {
193 ([self.subject, self.predicate, self.object], None)
194 }
195}
196#[derive(Debug, Clone, Eq)]
199pub struct MyTerm<'a> {
200 pub value: Cow<'a, str>,
201 ty: Option<TermKind>,
202 pub span: std::ops::Range<usize>,
203}
204
205impl Hash for MyTerm<'_> {
206 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
207 self.value.hash(state);
209 self.ty.hash(state);
210 }
211}
212
213impl PartialEq for MyTerm<'_> {
214 fn eq(&self, other: &Self) -> bool {
215 other.value == self.value && other.ty == self.ty
217 }
218}
219
220impl<'a> std::fmt::Display for MyTerm<'a> {
221 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
222 match self.kind() {
223 TermKind::Iri => write!(f, "<{}>", self.value),
224 TermKind::Literal => write!(f, "\"{}\"", self.value),
225 TermKind::BlankNode => write!(f, "_:{}", self.value),
226 TermKind::Triple => write!(f, "<{}>", self.value),
227 TermKind::Variable => write!(f, "?{}", self.value),
228 }
229 }
230}
231
232impl<'a> MyTerm<'a> {
233 pub fn to_owned(&self) -> MyTerm<'static> {
234 let value = Cow::Owned(self.value.to_string());
235 MyTerm {
236 value,
237 ty: self.ty.clone(),
238 span: self.span.clone(),
239 }
240 }
241 pub fn variable<T: Into<Cow<'a, str>>>(value: T, span: std::ops::Range<usize>) -> Self {
242 Self {
243 value: value.into(),
244 ty: TermKind::Variable.into(),
245 span,
246 }
247 }
248 pub fn named_node<T: Into<Cow<'a, str>>>(value: T, span: std::ops::Range<usize>) -> Self {
249 Self {
250 value: value.into(),
251 ty: TermKind::Iri.into(),
252 span,
253 }
254 }
255 pub fn blank_node<T: Into<Cow<'a, str>>>(value: T, span: std::ops::Range<usize>) -> Self {
256 Self {
257 value: value.into(),
258 ty: TermKind::BlankNode.into(),
259 span,
260 }
261 }
262 pub fn literal<T: Into<Cow<'a, str>>>(value: T, span: std::ops::Range<usize>) -> Self {
263 Self {
264 value: value.into(),
265 ty: TermKind::Literal.into(),
266 span,
267 }
268 }
269
270 pub fn invalid(span: std::ops::Range<usize>) -> Self {
271 Self {
272 value: Cow::default(),
273 ty: None,
274 span,
275 }
276 }
277
278 pub fn as_str(&'a self) -> &'a str {
279 &self.value
280 }
281}
282
283impl<'a> Term for MyTerm<'a> {
284 type BorrowTerm<'x>
285 = &'x Self
286 where
287 Self: 'x;
288
289 fn kind(&self) -> sophia_api::term::TermKind {
290 self.ty.unwrap_or(TermKind::Triple)
291 }
292
293 fn borrow_term(&self) -> Self::BorrowTerm<'_> {
294 self
295 }
296
297 fn iri(&self) -> Option<sophia_api::term::IriRef<sophia_api::MownStr>> {
298 self.is_iri()
299 .then(|| IriRef::new_unchecked(MownStr::from_str(&self.value)))
300 }
301
302 fn bnode_id(&self) -> Option<sophia_api::term::BnodeId<sophia_api::MownStr>> {
303 self.is_blank_node()
304 .then(|| BnodeId::new_unchecked(MownStr::from_str(&self.value)))
305 }
306
307 fn lexical_form(&self) -> Option<sophia_api::MownStr> {
308 self.is_literal().then(|| MownStr::from_str(&self.value))
309 }
310
311 fn datatype(&self) -> Option<sophia_api::term::IriRef<sophia_api::MownStr>> {
312 None
313 }
314
315 fn language_tag(&self) -> Option<sophia_api::term::LanguageTag<sophia_api::MownStr>> {
316 None
317 }
318
319 fn variable(&self) -> Option<sophia_api::term::VarName<sophia_api::MownStr>> {
320 panic!("MyTerm does not supported variables")
321 }
322
323 fn triple(&self) -> Option<[Self::BorrowTerm<'_>; 3]> {
324 panic!("MyTerm does not supported triples")
325 }
326
327 fn to_triple(self) -> Option<[Self; 3]>
328 where
329 Self: Sized,
330 {
331 panic!("MyTerm does not supported triples")
332 }
333}
334
335#[derive(Default, Debug)]
336pub struct Triples2<'a> {
337 pub base_url: String,
338 pub triples: Vec<MyQuad<'a>>,
339 pub base: Option<MyTerm<'a>>,
340}
341
342impl<'a> Triples2<'a> {
343 pub fn to_owned(&self) -> Triples2<'static> {
344 let triples = self.triples.iter().map(|q| q.to_owned()).collect();
345 let base: Option<MyTerm<'static>> = self.base.as_ref().map(|x| x.to_owned());
346
347 Triples2 {
348 base,
349 triples,
350 base_url: self.base_url.clone(),
351 }
352 }
353
354 pub fn imports(&self, cb: impl FnMut(IriRef<MownStr<'_>>) -> ()) {
355 if let Some(ref base) = self.base {
356 self.triples
357 .quads_matching([base], [owl::imports], Any, Any)
358 .flatten()
359 .flat_map(|s| s.o().iri())
360 .for_each(cb);
361 }
362 }
363
364 pub fn sub_class_of(&self, mut cb: impl FnMut(IriRef<MownStr<'_>>, IriRef<MownStr<'_>>) -> ()) {
365 self.triples
366 .quads_matching(Any, [rdfs::subClassOf], Any, Any)
367 .flatten()
368 .flat_map(|s| match (s.s().iri(), s.o().iri()) {
369 (Some(s), Some(o)) => Some((s, o)),
370 _ => None,
371 })
372 .for_each(|(x, y)| cb(x, y));
373 }
374}
375
376impl<'a> Deref for Triples2<'a> {
377 type Target = Vec<MyQuad<'a>>;
378
379 fn deref(&self) -> &Self::Target {
380 &self.triples
381 }
382}