1use std::{borrow::Cow, ops::Range};
2
3use lsp_core::prelude::*;
4
5use crate::lang::parser::{Json, ObjectMember};
6
7fn visit_obj(json: &Spanned<Json>, f: &mut dyn FnMut(&[Spanned<ObjectMember>], &Range<usize>)) {
8 match json.value() {
9 Json::Array(vec) => {
10 for v in vec.iter() {
11 visit_obj(v, f);
12 }
13 }
14 Json::Object(vec) => f(&vec, json.span()),
15 _ => {}
16 }
17}
18
19fn get_str(tok: &Token) -> Option<&str> {
20 match tok {
21 Token::Str(x, _) => Some(x),
22 _ => None,
23 }
24}
25
26fn correct_field(obj: &ObjectMember, field: &str) -> bool {
27 let f = obj.field();
28 match f.value() {
29 Token::Str(st, _) => st == field,
30 _ => false,
31 }
32}
33
34fn find_field<'a>(
35 mem: &'a [Spanned<ObjectMember>],
36 field: &str,
37) -> Option<(&'a Spanned<Json>, &'a Range<usize>)> {
38 mem.iter()
39 .find(|x| correct_field(x, field))
40 .and_then(|x| x.json_value().map(|y| (y, x.field().span())))
41}
42
43pub fn derive_prefixes(json: &Spanned<Json>, base: &lsp_types::Url) -> Prefixes {
44 let mut options: Vec<(Cow<str>, Cow<str>)> = Vec::new();
45
46 visit_obj(json, &mut |mem, _| {
48 let Some((ctx, _)) = find_field(mem, "@context") else {
49 return;
50 };
51
52 visit_obj(ctx, &mut |mems, _| {
53 for mem in mems {
54 let Some(key) = get_str(mem.field()) else {
55 continue;
56 };
57
58 let Some(value) = mem
59 .json_value()
60 .and_then(|x| x.as_ref().try_map(|x| x.token()))
61 .and_then(|x| x.try_map(|x| get_str(x)))
62 else {
63 continue;
64 };
65
66 options.push((Cow::Owned(key.to_string()), Cow::Owned(value.to_string())));
67 }
68 });
69 });
70
71 let mut changed = true;
73 let mut steps = 0;
74 while changed && steps < 5 {
75 changed = false;
76 for i in 0..options.len() {
77 let new_expaned = if let Some(pref) = options[i].1.find(':') {
78 let prefix = &options[i].1[..pref];
79 let Some((_, expaned)) = options.iter().find(|x| x.0 == prefix) else {
80 continue;
81 };
82 format!("{}{}", expaned, &options[i].1[pref + 1..])
83 } else {
84 continue;
85 };
86 options[i].1 = Cow::from(new_expaned);
87 changed = true;
88 }
89 steps += 1;
90 }
91 let mut out = Vec::new();
92
93 for (k, v) in options {
94 if let Some(url) = lsp_types::Url::parse(&v).ok() {
95 out.push(Prefix {
96 prefix: k.into_owned(),
97 url,
98 });
99 }
100 }
101
102 Prefixes(out, base.clone())
103}
104
105fn shorten_span(span: &Range<usize>) -> Range<usize> {
106 span.start + 1..span.end - 1
107}
108
109fn derive_triples_sub(
110 json: &Spanned<Json>,
111 prefixes: &Prefixes,
112 out: &mut Vec<MyQuad<'static>>,
113 bn_f: &mut dyn FnMut(Range<usize>) -> MyTerm<'static>,
114) -> Option<MyTerm<'static>> {
115 let mut subj_out = None;
116 visit_obj(json, &mut |mems, span| {
117 let subj = find_field(mems, "@id")
118 .and_then(|x| x.0.as_ref().try_map(|x| x.token()))
119 .and_then(|x| x.try_map(|x| prefixes.expand_json(x)))
120 .map(|Spanned(v, r)| MyTerm::named_node(v, shorten_span(&r)))
121 .unwrap_or_else(|| bn_f(span.clone()));
122
123 if let Some((ctx, _)) = find_field(mems, "@graph") {
124 derive_triples_sub(ctx, prefixes, out, bn_f);
125 }
126 if let Some((mem, span)) = find_field(mems, "@type") {
127 let object = match mem {
128 Spanned(Json::Token(tok), span) => {
129 if let Some(st) = prefixes.expand_json(&tok) {
130 MyTerm::named_node(st, span.clone())
131 } else {
132 MyTerm::invalid(span.clone())
133 }
134 }
135 json => derive_triples_sub(json, prefixes, out, bn_f)
136 .unwrap_or_else(|| MyTerm::invalid(json.span().clone())),
137 };
138
139 out.push(MyQuad {
140 subject: subj.clone(),
141 predicate: MyTerm::named_node(
142 "http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
143 span.clone(),
144 ),
145 object,
146 span: mem.span().clone(),
147 });
148 }
149 for mem in mems {
150 let field = mem.field();
151 let st = get_str(&field);
152 if st.map(|x| x.starts_with("@")).unwrap_or(false) {
153 continue;
154 }
155
156 let Some(pred) = prefixes
157 .expand_json(&field)
158 .or_else(|| st.map(|x| x.to_string()))
159 else {
160 continue;
161 };
162 let pred = MyTerm::named_node(pred, shorten_span(field.span()));
163
164 let object = match mem.json_value() {
166 None => MyTerm::invalid(0..0),
167 Some(Spanned(Json::Token(tok), span)) => match tok {
169 Token::Str(x, _) => MyTerm::literal(x.clone(), span.clone()),
170 _ => MyTerm::invalid(span.clone()),
171 },
172 Some(json) => derive_triples_sub(json, prefixes, out, bn_f)
173 .unwrap_or_else(|| MyTerm::invalid(json.span().clone())),
174 };
175
176 out.push(MyQuad {
177 subject: subj.clone(),
178 predicate: pred,
179 object,
180 span: mem.span().clone(),
181 });
182 }
183
184 subj_out = Some(subj);
185 });
186 subj_out
187}
188
189pub fn derive_triples(json: &Spanned<Json>, prefixes: &Prefixes) -> Vec<MyQuad<'static>> {
190 let mut bn_count = 0;
191 let mut bn = move |span| {
192 let out = MyTerm::blank_node(format!("_:{}", bn_count), span);
193 bn_count += 1;
194 out
195 };
196 let mut out = Vec::new();
197 derive_triples_sub(json, prefixes, &mut out, &mut bn);
198 out
199}
200
201#[cfg(test)]
202mod tests {
203
204 use lsp_core::prelude::{MyQuad, Spanned};
205 use sophia_api::term::{Term, TermKind};
206
207 use super::{derive_prefixes, derive_triples};
208 use crate::lang::{
209 parser::{parse, Json},
210 tokenizer::tokenize,
211 };
212
213 fn parse_json(st: &str) -> Option<Spanned<Json>> {
214 let (tok, es) = tokenize(st);
215 if !es.is_empty() {
216 return None;
217 }
218 let (jsonld, es) = parse(st, tok);
219 if !es.is_empty() {
220 return None;
221 }
222 Some(jsonld)
223 }
224
225 #[test]
226 fn simple_context_foaf_1() {
227 let st = r#" {
228 "@context": {"foaf": "http://xmlns.com/foaf/0.1/"},
229 "@id": "http://example.com/ns#me",
230 "foaf:name": "Arthur"
231 } "#;
232 let url = lsp_types::Url::parse("memory://test.jsonld").unwrap();
233
234 let json = parse_json(st).expect("valid json");
235 let prefixes = derive_prefixes(&json, &url);
236
237 assert_eq!(prefixes.0.len(), 1);
238 let foaf_prefix = prefixes
239 .iter()
240 .find(|x| x.prefix == "foaf")
241 .expect("foaf prefix");
242 assert_eq!(foaf_prefix.url.as_str(), "http://xmlns.com/foaf/0.1/");
243 }
244
245 #[test]
246 fn simple_context_foaf_2() {
247 let st = r#" {
248 "@context": [ {"foaf": "http://xmlns.com/foaf/0.1/"} ],
249 "@id": "http://example.com/ns#me",
250 "foaf:name": "Arthur"
251 } "#;
252 let url = lsp_types::Url::parse("memory://test.jsonld").unwrap();
253
254 let json = parse_json(st).expect("valid json");
255 let prefixes = derive_prefixes(&json, &url);
256
257 assert_eq!(prefixes.0.len(), 1);
258
259 let foaf_prefix = prefixes
260 .iter()
261 .find(|x| x.prefix == "foaf")
262 .expect("foaf prefix");
263 assert_eq!(foaf_prefix.url.as_str(), "http://xmlns.com/foaf/0.1/");
264 }
265
266 #[test]
267 fn simple_context_foaf_3() {
268 let st = r#" {
269 "@context": {"foaf": "http://xmlns.com/foaf/0.1/", "name": "foaf:name"},
270 "@id": "http://example.com/ns#me",
271 "name": "Arthur"
272 } "#;
273 let url = lsp_types::Url::parse("memory://test.jsonld").unwrap();
274
275 let json = parse_json(st).expect("valid json");
276 let prefixes = derive_prefixes(&json, &url);
277
278 assert_eq!(prefixes.0.len(), 2);
279 let name_prefix = prefixes
280 .iter()
281 .find(|x| x.prefix == "name")
282 .expect("name prefix");
283 assert_eq!(name_prefix.url.as_str(), "http://xmlns.com/foaf/0.1/name");
284
285 let foaf_prefix = prefixes
286 .iter()
287 .find(|x| x.prefix == "foaf")
288 .expect("foaf prefix");
289 assert_eq!(foaf_prefix.url.as_str(), "http://xmlns.com/foaf/0.1/");
290 }
291
292 #[test]
293 fn simple_context_foaf_3_ignore_extra_ctx() {
294 let st = r#" {
295 "@context": [ {"foaf": "http://xmlns.com/foaf/0.1/"}, "http://xmlns.com/foaf/0.1/context.jsonld" ],
296 "@id": "http://example.com/ns#me",
297 "foaf:name": "Arthur"
298 } "#;
299 let url = lsp_types::Url::parse("memory://test.jsonld").unwrap();
300
301 let json = parse_json(st).expect("valid json");
302 let prefixes = derive_prefixes(&json, &url);
303
304 assert_eq!(prefixes.0.len(), 1);
305 let foaf_prefix = prefixes
306 .iter()
307 .find(|x| x.prefix == "foaf")
308 .expect("foaf prefix");
309 assert_eq!(foaf_prefix.url.as_str(), "http://xmlns.com/foaf/0.1/");
310 }
311
312 #[test]
313 fn derive_simple_triples() {
314 let st = r#" {
315 "@context": {"foaf": "http://xmlns.com/foaf/0.1/"} ,
316 "@id": "http://example.com/ns#me",
317 "foaf:name": "Arthur"
318 } "#;
319 let url = lsp_types::Url::parse("memory://test.jsonld").unwrap();
320
321 let json = parse_json(st).expect("valid json");
322 let prefixes = derive_prefixes(&json, &url);
323 let triples = derive_triples(&json, &prefixes);
324
325 assert_eq!(triples.len(), 1);
326 let MyQuad {
327 subject,
328 predicate,
329 object,
330 ..
331 } = &triples[0];
332
333 assert_eq!(subject.as_str(), "http://example.com/ns#me");
334 assert_eq!(subject.kind(), TermKind::Iri);
335 assert_eq!(predicate.as_str(), "http://xmlns.com/foaf/0.1/name");
336 assert_eq!(predicate.kind(), TermKind::Iri);
337 assert_eq!(object.as_str(), "Arthur");
338 assert_eq!(object.kind(), TermKind::Literal);
339 }
340
341 #[test]
342 fn derive_simple_triples_type() {
343 let st = r#" {
344 "@context": {"foaf": "http://xmlns.com/foaf/0.1/"} ,
345 "@id": "http://example.com/ns#me",
346 "@type": "http://example.com/ns#my_type"
347 } "#;
348 let url = lsp_types::Url::parse("memory://test.jsonld").unwrap();
349
350 let json = parse_json(st).expect("valid json");
351 let prefixes = derive_prefixes(&json, &url);
352 let triples = derive_triples(&json, &prefixes);
353
354 assert_eq!(triples.len(), 1);
355 let MyQuad {
356 subject,
357 predicate,
358 object,
359 ..
360 } = &triples[0];
361
362 assert_eq!(subject.as_str(), "http://example.com/ns#me");
363 assert_eq!(subject.kind(), TermKind::Iri);
364 assert_eq!(
365 predicate.as_str(),
366 "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"
367 );
368 assert_eq!(predicate.kind(), TermKind::Iri);
369 assert_eq!(object.as_str(), "http://example.com/ns#my_type");
370 assert_eq!(object.kind(), TermKind::Iri);
371 }
372
373 #[test]
374 fn derive_simple_triples_bn() {
375 let st = r#" {
376 "@context": {"foaf": "http://xmlns.com/foaf/0.1/"} ,
377 "foaf:name": "Arthur"
378 } "#;
379 let url = lsp_types::Url::parse("memory://test.jsonld").unwrap();
380
381 let json = parse_json(st).expect("valid json");
382 let prefixes = derive_prefixes(&json, &url);
383 let triples = derive_triples(&json, &prefixes);
384
385 assert_eq!(triples.len(), 1);
386 let MyQuad {
387 subject,
388 predicate,
389 object,
390 ..
391 } = &triples[0];
392
393 assert_eq!(subject.kind(), TermKind::BlankNode);
394 assert_eq!(predicate.as_str(), "http://xmlns.com/foaf/0.1/name");
395 assert_eq!(predicate.kind(), TermKind::Iri);
396 assert_eq!(object.as_str(), "Arthur");
397 assert_eq!(object.kind(), TermKind::Literal);
398 }
399
400 #[test]
401 fn derive_simple_triples_graph() {
402 let st = r#" {
403 "@context": {"foaf": "http://xmlns.com/foaf/0.1/"} ,
404 "@graph": [ {
405 "@id": "http://example.com/ns#me",
406 "foaf:name": "Arthur"
407 } ]
408 } "#;
409 let url = lsp_types::Url::parse("memory://test.jsonld").unwrap();
410
411 let json = parse_json(st).expect("valid json");
412 let prefixes = derive_prefixes(&json, &url);
413 let triples = derive_triples(&json, &prefixes);
414
415 assert_eq!(triples.len(), 1);
416 let MyQuad {
417 subject,
418 predicate,
419 object,
420 ..
421 } = &triples[0];
422
423 assert_eq!(subject.as_str(), "http://example.com/ns#me");
424 assert_eq!(subject.kind(), TermKind::Iri);
425 assert_eq!(predicate.as_str(), "http://xmlns.com/foaf/0.1/name");
426 assert_eq!(predicate.kind(), TermKind::Iri);
427 assert_eq!(object.as_str(), "Arthur");
428 assert_eq!(object.kind(), TermKind::Literal);
429 }
430
431 #[test]
432 fn derive_simple_triples_bn_graph() {
433 let st = r#" {
434 "@context": {"foaf": "http://xmlns.com/foaf/0.1/"} ,
435 "@graph": [ {
436 "foaf:name": "Arthur"
437 } ]
438 } "#;
439 let url = lsp_types::Url::parse("memory://test.jsonld").unwrap();
440
441 let json = parse_json(st).expect("valid json");
442 let prefixes = derive_prefixes(&json, &url);
443 let triples = derive_triples(&json, &prefixes);
444
445 assert_eq!(triples.len(), 1);
446 let MyQuad {
447 subject,
448 predicate,
449 object,
450 ..
451 } = &triples[0];
452
453 assert_eq!(subject.kind(), TermKind::BlankNode);
454 assert_eq!(predicate.as_str(), "http://xmlns.com/foaf/0.1/name");
455 assert_eq!(predicate.kind(), TermKind::Iri);
456 assert_eq!(object.as_str(), "Arthur");
457 assert_eq!(object.kind(), TermKind::Literal);
458 }
459
460 #[test]
461 fn derive_simple_triples_deep() {
462 let st = r#" {
463 "@context": {"foaf": "http://xmlns.com/foaf/0.1/"} ,
464 "@id": "http://example.com/ns#me",
465 "foaf:friend": {
466 "foaf:name": "Arthur",
467 "foaf:friend": {
468 "foaf:name": "Julian"
469 }
470 }
471 } "#;
472 let url = lsp_types::Url::parse("memory://test.jsonld").unwrap();
473
474 let json = parse_json(st).expect("valid json");
475 let prefixes = derive_prefixes(&json, &url);
476 let triples = derive_triples(&json, &prefixes);
477
478 assert_eq!(triples.len(), 4);
479 let MyQuad {
480 subject,
481 predicate,
482 object,
483 ..
484 } = triples
485 .iter()
486 .find(|x| x.subject.as_str() == "http://example.com/ns#me")
487 .expect("my triple");
488 assert_eq!(subject.as_str(), "http://example.com/ns#me");
489 assert_eq!(subject.kind(), TermKind::Iri);
490 assert_eq!(predicate.as_str(), "http://xmlns.com/foaf/0.1/friend");
491 assert_eq!(predicate.kind(), TermKind::Iri);
492 assert_eq!(object.kind(), TermKind::BlankNode);
493
494 let friend: Vec<_> = triples.iter().filter(|x| &x.subject == object).collect();
495 assert_eq!(friend.len(), 2);
496 let friend_name = &friend
497 .iter()
498 .find(|x| x.predicate.as_str() == "http://xmlns.com/foaf/0.1/name")
499 .expect("friend name")
500 .object;
501 assert_eq!(friend_name.as_str(), "Arthur");
502 assert_eq!(friend_name.kind(), TermKind::Literal);
503
504 let friend_friend = &friend
505 .iter()
506 .find(|x| x.predicate.as_str() == "http://xmlns.com/foaf/0.1/friend")
507 .expect("friend name")
508 .object;
509 assert_eq!(friend_friend.kind(), TermKind::BlankNode);
510
511 let friend_friend: Vec<_> = triples
512 .iter()
513 .filter(|x| &x.subject == friend_friend)
514 .collect();
515 assert_eq!(friend_friend.len(), 1);
516 assert_eq!(friend_friend[0].object.as_str(), "Julian");
517 assert_eq!(friend_friend[0].object.kind(), TermKind::Literal);
518 }
519}