1use std::{borrow::Cow, collections::HashSet};
2
3pub use rdf_parsers::model::{
7 Base, BlankNode, Literal, NamedNode, RDFLiteral, StringStyle, Term, Triple, Turtle,
8 TurtlePrefix, Variable, PO,
9};
10use swls_core::{
11 lsp_types::Url,
12 prelude::{MyQuad, MyTerm, Spanned, TermContext, Triples2},
13 util::resolve_iri,
14};
15use tracing::warn;
16
17pub trait Based {
20 fn get_base(&self) -> &str;
21 fn prefixes(&self) -> &[Spanned<TurtlePrefix>];
22}
23
24impl Based for Turtle {
25 fn get_base(&self) -> &str {
26 self.set_base.as_deref().unwrap_or("")
27 }
28 fn prefixes(&self) -> &[Spanned<TurtlePrefix>] {
29 &self.prefixes
30 }
31}
32
33pub trait NamedNodeExt {
36 fn expand<T: Based>(&self, turtle: &T) -> Option<String>;
38 fn expand_step<T: Based>(&self, turtle: &T, done: HashSet<String>) -> Option<String>;
39}
40
41impl NamedNodeExt for NamedNode {
42 fn expand<T: Based>(&self, turtle: &T) -> Option<String> {
43 let out = self.expand_step(turtle, HashSet::new())?;
44 let base = Url::parse(turtle.get_base()).ok()?;
45 let url = base.join(&out).ok()?;
46 Some(url.to_string())
47 }
48
49 fn expand_step<T: Based>(&self, turtle: &T, mut done: HashSet<String>) -> Option<String> {
50 match self {
51 Self::Full(s, _) => Some(s.clone()),
52 Self::Prefixed {
53 prefix,
54 value,
55 computed,
56 ..
57 } => {
58 if let Some(computed) = computed {
63 return Some(computed.clone());
64 }
65 if done.contains(prefix.as_str()) {
66 return None;
67 }
68 done.insert(prefix.clone());
69 let prefix_entry = turtle
70 .prefixes()
71 .iter()
72 .find(|x| x.prefix.as_str() == prefix.as_str())?;
73 let expanded = NamedNodeExt::expand_step(prefix_entry.value.value(), turtle, done)?;
74 Some(format!("{}{}", expanded, value))
75 }
76 Self::A(_) => Some("http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string()),
77 Self::Invalid => None,
78 }
79 }
80}
81
82pub struct TriplesBuilder<'a, T> {
85 pub triples: Vec<MyQuad<'a>>,
86 blank_node: Box<dyn FnMut(std::ops::Range<usize>) -> MyTerm<'a>>,
87 base: String,
88 based: &'a T,
89}
90
91impl<'a, T: Based> TriplesBuilder<'a, T> {
92 pub fn new(based: &'a T, base: String) -> Self {
93 let mut count = 0;
94 let blank_node = Box::new(move |span: std::ops::Range<usize>| {
95 count += 1;
96 MyTerm::blank_node(format!("internal_bnode_{}", count), span)
97 });
98 Self {
99 triples: vec![],
100 blank_node,
101 base,
102 based,
103 }
104 }
105
106 fn handle_po(
107 &mut self,
108 pos: &'a [Spanned<PO>],
109 span: std::ops::Range<usize>,
110 subject: MyTerm<'a>,
111 ) -> Result<(), TurtleSimpleError> {
112 if pos.is_empty() {
113 self.triples.push(MyQuad {
117 subject: subject.clone(),
118 predicate: MyTerm::invalid(0..0),
119 object: MyTerm::invalid(0..0),
120 span: span.clone(),
121 });
122 }
123 let mut first = true;
124 for Spanned(PO { predicate, object }, span2) in pos.iter() {
125 let this_span = if first {
126 first = false;
127 span.clone()
128 } else {
129 span2.clone()
130 };
131
132 let predicate_term = match predicate.value() {
133 Term::NamedNode(NamedNode::Invalid) => MyTerm::invalid(predicate.span().clone()),
134 Term::NamedNode(nn) => {
135 if let Some(node) = NamedNodeExt::expand_step(nn, self.based, HashSet::new())
136 .map(|n| resolve_iri(&self.base, &n))
137 {
138 MyTerm::named_node(node, predicate.span().clone())
139 } else {
140 MyTerm::invalid(predicate.span().clone())
141 }
142 }
143 _ => MyTerm::invalid(predicate.span().clone()),
144 };
145
146 if object.is_empty() {
147 let end = predicate.span().end;
151 self.triples.push(MyQuad {
152 subject: subject.clone(),
153 predicate: predicate_term.clone(),
154 object: MyTerm::invalid(end..end),
155 span: this_span.clone(),
156 });
157 }
158 let mut first_object = true;
159 for o in object.iter() {
160 let this_span = if first_object {
161 first_object = false;
162 this_span.clone()
163 } else {
164 o.span().clone()
165 };
166 let obj = self.term_to_my_term(Ok(o.as_ref()))?;
167 self.triples.push(MyQuad {
168 subject: subject.clone(),
169 predicate: predicate_term.clone(),
170 object: obj,
171 span: this_span,
172 });
173 }
174 }
175 Ok(())
176 }
177
178 fn term_to_my_term(
179 &mut self,
180 term: Result<Spanned<&'a Term>, MyTerm<'a>>,
181 ) -> Result<MyTerm<'a>, TurtleSimpleError> {
182 let object = match term {
183 Ok(Spanned(Term::Variable(Variable(var, _)), span)) => MyTerm::variable(var, span),
184 Ok(Spanned(Term::NamedNode(NamedNode::Invalid), span)) => MyTerm::invalid(span),
185 Ok(Spanned(Term::NamedNode(nn), span)) => {
186 match NamedNodeExt::expand_step(nn, self.based, HashSet::new()) {
187 Some(n) => MyTerm::named_node(resolve_iri(&self.base, &n), span),
188 None => MyTerm::invalid(span),
189 }
190 }
191 Ok(Spanned(Term::Literal(Literal::RDF(lit)), span)) => {
192 let term_context = match (&lit.lang, &lit.ty) {
193 (Some(l), _) => TermContext::LangTag(Cow::Owned(l.value().clone())),
194 (_, Some(dt)) => {
195 if let Some(dt) =
196 NamedNodeExt::expand_step(dt.value(), self.based, HashSet::new())
197 {
198 TermContext::DataType(dt.into())
199 } else {
200 TermContext::None
201 }
202 }
203 _ => TermContext::None,
204 };
205 MyTerm::literal(lit.value.to_string(), span, term_context)
206 }
207 Ok(Spanned(Term::Literal(Literal::Boolean(bool)), span)) => {
208 let term_context =
209 TermContext::DataType("http://www.w3.org/2001/XMLSchema#boolean".into());
210 MyTerm::literal(bool.to_string(), span, term_context)
211 }
212 Ok(Spanned(Term::Literal(Literal::Numeric(num)), span)) => {
213 let term_context =
214 TermContext::DataType("http://www.w3.org/2001/XMLSchema#integer".into());
215 MyTerm::literal(num.to_string(), span, term_context)
216 }
217 Ok(Spanned(Term::BlankNode(bn), span)) => match bn {
218 BlankNode::Named(v, _) => MyTerm::blank_node(v, span),
219 BlankNode::Unnamed(v, _, _) => {
220 let out = (self.blank_node)(span.clone());
221 self.handle_po(v, span, out.clone())?;
222 out
223 }
224 BlankNode::Invalid => {
225 return Err(TurtleSimpleError::UnexpectedBase(
226 "Unexpected invalid blank for object",
227 ))
228 }
229 },
230 Ok(Spanned(Term::Collection(terms), span)) => self.handle_collection(terms, span)?,
231 Ok(Spanned(Term::Invalid, span)) => MyTerm::invalid(span),
232 Err(x) => x,
233 };
234 Ok(object)
235 }
236
237 fn handle_collection(
238 &mut self,
239 collection: &'a [Spanned<Term>],
240 span: std::ops::Range<usize>,
241 ) -> Result<MyTerm<'a>, TurtleSimpleError> {
242 let mut prev = MyTerm::named_node(
243 "http://www.w3.org/1999/02/22-rdf-syntax-ns#nil",
244 span.end..span.end,
245 );
246 for Spanned(term, s) in collection.iter().rev() {
247 let next = (self.blank_node)(s.clone());
248 let obj = self.term_to_my_term(Ok(Spanned(term, s.clone())))?;
249 self.triples.push(MyQuad {
250 subject: next.clone(),
251 predicate: MyTerm::named_node(
252 "http://www.w3.org/1999/02/22-rdf-syntax-ns#first",
253 prev.span.start..prev.span.start,
254 ),
255 object: obj,
256 span: span.clone(),
257 });
258 self.triples.push(MyQuad {
259 subject: next.clone(),
260 predicate: MyTerm::named_node(
261 "http://www.w3.org/1999/02/22-rdf-syntax-ns#rest",
262 s.start..s.start,
263 ),
264 object: prev,
265 span: s.clone(),
266 });
267 prev = next;
268 }
269 Ok(prev)
270 }
271
272 pub fn ingest(
273 &mut self,
274 Spanned(ref triple, span): &'a Spanned<Triple>,
275 ) -> Result<(), TurtleSimpleError> {
276 let sub = match triple.subject.value() {
277 Term::BlankNode(BlankNode::Named(vs, _)) => {
278 MyTerm::blank_node(vs, triple.subject.span().clone())
279 }
280 Term::BlankNode(BlankNode::Unnamed(vs, _, _)) => {
281 let out = (self.blank_node)(triple.subject.span().clone());
282 self.handle_po(vs, triple.subject.span().clone(), out.clone())?;
283 out
284 }
285 Term::NamedNode(NamedNode::Invalid) => MyTerm::invalid(triple.subject.span().clone()),
286 Term::NamedNode(nn) => {
287 match NamedNodeExt::expand_step(nn, self.based, HashSet::new()) {
288 Some(n) => MyTerm::named_node(
289 resolve_iri(&self.base, &n),
290 triple.subject.span().clone(),
291 ),
292 None => MyTerm::invalid(triple.subject.span().clone()),
293 }
294 }
295 Term::Invalid => MyTerm::invalid(triple.subject.span().clone()),
296 Term::Variable(var) => MyTerm::variable(&var.0, triple.subject.span().clone()),
297 x => {
298 warn!("Failed, unexpected {}", x.ty());
299 return Err(TurtleSimpleError::UnexpectedBaseString(format!(
300 "Unexpected {}",
301 x.ty()
302 )));
303 }
304 };
305 self.handle_po(&triple.po, span.clone(), sub)?;
306 Ok(())
307 }
308}
309
310#[derive(Debug)]
311pub enum TurtleSimpleError {
312 UnexpectedBase(&'static str),
313 UnexpectedBaseString(String),
314}
315
316pub trait TurtleExt {
318 fn get_simple_triples<'a>(&'a self) -> Result<Triples2<'a>, TurtleSimpleError>;
319 fn into_triples<'a>(&self, triples: Vec<MyQuad<'a>>) -> Triples2<'a>;
320 fn shorten(&self, url: &str) -> Option<String>;
321}
322
323pub trait TurtlePrefixExt {
325 fn shorten<T: Based>(&self, turtle: &T, url: &str) -> Option<String>;
326}
327
328impl TurtlePrefixExt for TurtlePrefix {
329 fn shorten<T: Based>(&self, turtle: &T, url: &str) -> Option<String> {
330 let prefix_url = NamedNodeExt::expand(self.value.value(), turtle)?;
331 let short = url.strip_prefix(&prefix_url)?;
332 Some(format!("{}:{}", self.prefix.as_str(), short))
333 }
334}
335
336impl TurtleExt for Turtle {
337 fn get_simple_triples<'a>(&'a self) -> Result<Triples2<'a>, TurtleSimpleError> {
338 let base = match &self.base {
339 Some(Spanned(Base(_, Spanned(named_node, _)), _)) => {
340 NamedNodeExt::expand_step(named_node, self, HashSet::new()).ok_or(
341 TurtleSimpleError::UnexpectedBase("Expected valid named node base"),
342 )?
343 }
344 None => self.set_base.clone().unwrap_or_default(),
345 };
346
347 let mut builder = TriplesBuilder::new(self, base);
348
349 for t in &self.triples {
350 if let Err(e) = builder.ingest(t) {
351 tracing::error!("Failed parsing triple {}: {:?}", t.value(), e);
352 }
353 }
354
355 Ok(self.into_triples(builder.triples))
356 }
357
358 fn into_triples<'a>(&self, triples: Vec<MyQuad<'a>>) -> Triples2<'a> {
359 tracing::debug!("Results in {} triples", triples.len());
360 let base = match &self.base {
361 Some(Spanned(Base(_, Spanned(named_node, span)), _)) => {
362 NamedNodeExt::expand_step(named_node, self, HashSet::new())
363 .map(|st| MyTerm::named_node(st, span.clone()))
364 }
365 None => Some(MyTerm::named_node(
366 self.set_base.clone().unwrap_or_default(),
367 0..0,
368 )),
369 };
370 let base_url = self.set_base.clone().unwrap_or_default();
371 Triples2 {
372 triples,
373 base,
374 base_url,
375 }
376 }
377
378 fn shorten(&self, url: &str) -> Option<String> {
379 self.prefixes
380 .iter()
381 .find_map(|p| TurtlePrefixExt::shorten(p.value(), self, url))
382 }
383}