json_ld_core/rdf/
mod.rs

1use std::str::FromStr;
2
3use crate::{object::value, Direction, Id, Indexed, IndexedObject, Node, Object, ValidId};
4use iref::{Iri, IriBuf};
5use json_syntax::Print;
6use langtag::LangTagBuf;
7use rdf_types::{
8	vocabulary::{IriVocabularyMut, LiteralVocabularyMut},
9	Generator, Literal, Vocabulary,
10};
11use smallvec::SmallVec;
12use static_iref::iri;
13
14mod quad;
15pub use quad::*;
16
17pub const RDF_TYPE: &Iri = iri!("http://www.w3.org/1999/02/22-rdf-syntax-ns#type");
18pub const RDF_FIRST: &Iri = iri!("http://www.w3.org/1999/02/22-rdf-syntax-ns#first");
19pub const RDF_REST: &Iri = iri!("http://www.w3.org/1999/02/22-rdf-syntax-ns#rest");
20pub const RDF_VALUE: &Iri = iri!("http://www.w3.org/1999/02/22-rdf-syntax-ns#value");
21pub const RDF_DIRECTION: &Iri = iri!("http://www.w3.org/1999/02/22-rdf-syntax-ns#direction");
22pub const RDF_JSON: &Iri = iri!("http://www.w3.org/1999/02/22-rdf-syntax-ns#JSON");
23/// IRI of the `http://www.w3.org/1999/02/22-rdf-syntax-ns#nil` value.
24pub const RDF_NIL: &Iri = iri!("http://www.w3.org/1999/02/22-rdf-syntax-ns#nil");
25
26pub const XSD_BOOLEAN: &Iri = iri!("http://www.w3.org/2001/XMLSchema#boolean");
27pub const XSD_INTEGER: &Iri = iri!("http://www.w3.org/2001/XMLSchema#integer");
28pub const XSD_DOUBLE: &Iri = iri!("http://www.w3.org/2001/XMLSchema#double");
29pub const XSD_STRING: &Iri = iri!("http://www.w3.org/2001/XMLSchema#string");
30
31/// JSON-LD to RDF triple.
32pub type Triple<T, B, L> = rdf_types::Triple<ValidId<T, B>, ValidId<T, B>, Value<T, B, L>>;
33
34impl<T: Clone, B: Clone> Id<T, B> {
35	fn rdf_value<L>(&self) -> Option<Value<T, B, L>> {
36		match self {
37			Id::Valid(id) => Some(Value::Id(id.clone())),
38			Id::Invalid(_) => None,
39		}
40	}
41}
42
43/// Direction representation method.
44///
45/// Used by the RDF serializer to decide how to encode
46/// [`Direction`](crate::Direction)s.
47#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
48pub enum RdfDirection {
49	/// Encode direction in the string value type IRI using the
50	/// `https://www.w3.org/ns/i18n#` prefix.
51	///
52	/// If a language tag is present the IRI will be of the form
53	/// `https://www.w3.org/ns/i18n#language_direction` or simply
54	/// `https://www.w3.org/ns/i18n#direction` otherwise where `direction` is
55	/// either `rtl` or `ltr`.
56	I18nDatatype,
57
58	/// Encode the direction using a compound literal value.
59	///
60	/// In this case the direction tagged string is encoded with a fresh blank
61	/// node identifier `_:b` and the following triples:
62	/// ```nquads
63	/// _:b http://www.w3.org/1999/02/22-rdf-syntax-ns#value value@language
64	/// _:b http://www.w3.org/1999/02/22-rdf-syntax-ns#direction direction
65	/// ```
66	/// where `direction` is either `rtl` or `ltr`.
67	CompoundLiteral,
68}
69
70#[derive(Debug, Clone)]
71pub struct InvalidRdfDirection(pub String);
72
73impl FromStr for RdfDirection {
74	type Err = InvalidRdfDirection;
75
76	fn from_str(s: &str) -> Result<Self, Self::Err> {
77		match s {
78			"i18n-datatype" => Ok(Self::I18nDatatype),
79			"compound-literal" => Ok(Self::CompoundLiteral),
80			_ => Err(InvalidRdfDirection(s.to_string())),
81		}
82	}
83}
84
85impl<'a> TryFrom<&'a str> for RdfDirection {
86	type Error = InvalidRdfDirection;
87
88	fn try_from(value: &'a str) -> Result<Self, Self::Error> {
89		value.parse()
90	}
91}
92
93/// Iterator over the triples of a compound literal representing a language
94/// tagged string with direction.
95pub struct CompoundLiteralTriples<T, B, L> {
96	/// Compound literal identifier.
97	id: ValidId<T, B>,
98
99	/// String value.
100	value: Option<Value<T, B, L>>,
101
102	/// Direction value.
103	direction: Option<Value<T, B, L>>,
104}
105
106impl<T: Clone, B: Clone, L: Clone> CompoundLiteralTriples<T, B, L> {
107	fn next(&mut self, vocabulary: &mut impl IriVocabularyMut<Iri = T>) -> Option<Triple<T, B, L>> {
108		if let Some(value) = self.value.take() {
109			return Some(rdf_types::Triple(
110				self.id.clone(),
111				ValidId::Iri(vocabulary.insert(RDF_VALUE)),
112				value,
113			));
114		}
115
116		if let Some(direction) = self.direction.take() {
117			return Some(rdf_types::Triple(
118				self.id.clone(),
119				ValidId::Iri(vocabulary.insert(RDF_DIRECTION)),
120				direction,
121			));
122		}
123
124		None
125	}
126}
127
128/// Compound literal.
129pub struct CompoundLiteral<T, B, L> {
130	value: Value<T, B, L>,
131	triples: Option<CompoundLiteralTriples<T, B, L>>,
132}
133
134impl<T: Clone> crate::object::Value<T> {
135	fn rdf_value_with<V, G: Generator<V>>(
136		&self,
137		vocabulary: &mut V,
138		generator: &mut G,
139		rdf_direction: Option<RdfDirection>,
140	) -> Option<CompoundLiteral<T, V::BlankId, V::Literal>>
141	where
142		V: Vocabulary<Iri = T> + IriVocabularyMut + LiteralVocabularyMut,
143	{
144		match self {
145			Self::Json(json) => {
146				let ty = vocabulary.insert(RDF_JSON);
147				Some(CompoundLiteral {
148					value: Value::Literal(vocabulary.insert_owned_literal(Literal::new(
149						json.compact_print().to_string(),
150						rdf_types::LiteralType::Any(ty),
151					))),
152					triples: None,
153				})
154			}
155			Self::LangString(lang_string) => {
156				let (string, language, direction) = lang_string.parts();
157
158				let language = match language {
159					Some(language) => match language.as_well_formed() {
160						Some(tag) => Some(tag.to_owned()),
161						None => return None,
162					},
163					None => None,
164				};
165
166				match direction {
167					Some(direction) => match rdf_direction {
168						Some(RdfDirection::I18nDatatype) => {
169							let ty = vocabulary.insert(i18n(language, *direction).as_iri());
170							Some(CompoundLiteral {
171								value: Value::Literal(vocabulary.insert_owned_literal(
172									Literal::new(
173										string.to_string(),
174										rdf_types::LiteralType::Any(ty),
175									),
176								)),
177								triples: None,
178							})
179						}
180						Some(RdfDirection::CompoundLiteral) => {
181							let id = generator.next(vocabulary);
182							Some(CompoundLiteral {
183								value: id.into_term(),
184								triples: None,
185							})
186						}
187						None => match language {
188							Some(tag) => Some(CompoundLiteral {
189								value: Value::Literal(vocabulary.insert_owned_literal(
190									Literal::new(
191										string.to_string(),
192										rdf_types::LiteralType::LangString(tag),
193									),
194								)),
195								triples: None,
196							}),
197							None => {
198								let ty = vocabulary.insert(XSD_STRING);
199								Some(CompoundLiteral {
200									value: Value::Literal(vocabulary.insert_owned_literal(
201										Literal::new(
202											string.to_string(),
203											rdf_types::LiteralType::Any(ty),
204										),
205									)),
206									triples: None,
207								})
208							}
209						},
210					},
211					None => match language {
212						Some(tag) => Some(CompoundLiteral {
213							value: Value::Literal(vocabulary.insert_owned_literal(Literal::new(
214								string.to_string(),
215								rdf_types::LiteralType::LangString(tag),
216							))),
217							triples: None,
218						}),
219						None => {
220							let ty = vocabulary.insert(XSD_STRING);
221							Some(CompoundLiteral {
222								value: Value::Literal(vocabulary.insert_owned_literal(
223									Literal::new(
224										string.to_string(),
225										rdf_types::LiteralType::Any(ty),
226									),
227								)),
228								triples: None,
229							})
230						}
231					},
232				}
233			}
234			Self::Literal(lit, ty) => {
235				let (rdf_lit, prefered_rdf_ty) = match lit {
236					value::Literal::Boolean(b) => {
237						let lit = if *b {
238							"true".to_string()
239						} else {
240							"false".to_string()
241						};
242
243						(lit, Some(vocabulary.insert(XSD_BOOLEAN)))
244					}
245					value::Literal::Null => ("null".to_string(), None),
246					value::Literal::Number(n) => {
247						if n.is_i64()
248							&& !ty
249								.as_ref()
250								.map(|t| vocabulary.iri(t).unwrap() == XSD_DOUBLE)
251								.unwrap_or(false)
252						{
253							(n.to_string(), Some(vocabulary.insert(XSD_INTEGER)))
254						} else {
255							(
256								pretty_dtoa::dtoa(n.as_f64_lossy(), XSD_CANONICAL_FLOAT),
257								Some(vocabulary.insert(XSD_DOUBLE)),
258							)
259						}
260					}
261					value::Literal::String(s) => (s.to_string(), None),
262				};
263
264				let rdf_ty = match ty {
265					Some(id) => Some(id.clone()),
266					None => prefered_rdf_ty,
267				};
268
269				Some(CompoundLiteral {
270					value: match rdf_ty {
271						Some(ty) => Value::Literal(vocabulary.insert_owned_literal(Literal::new(
272							rdf_lit,
273							rdf_types::LiteralType::Any(ty),
274						))),
275						None => {
276							let ty = vocabulary.insert(XSD_STRING);
277							Value::Literal(vocabulary.insert_owned_literal(Literal::new(
278								rdf_lit,
279								rdf_types::LiteralType::Any(ty),
280							)))
281						}
282					},
283					triples: None,
284				})
285			}
286		}
287	}
288}
289
290// <https://www.w3.org/TR/xmlschema11-2/#f-doubleLexmap>
291const XSD_CANONICAL_FLOAT: pretty_dtoa::FmtFloatConfig = pretty_dtoa::FmtFloatConfig::default()
292	.force_e_notation()
293	.capitalize_e(true);
294
295impl<T: Clone, B: Clone> Node<T, B> {
296	fn rdf_value<L>(&self) -> Option<Value<T, B, L>> {
297		self.id.as_ref().and_then(Id::rdf_value)
298	}
299}
300
301impl<T: Clone, B: Clone> Object<T, B> {
302	fn rdf_value_with<V, G: Generator<V>>(
303		&self,
304		vocabulary: &mut V,
305		generator: &mut G,
306		rdf_direction: Option<RdfDirection>,
307	) -> Option<CompoundValue<T, B, V::Literal>>
308	where
309		V: Vocabulary<Iri = T, BlankId = B> + IriVocabularyMut + LiteralVocabularyMut,
310	{
311		match self {
312			Self::Value(value) => value
313				.rdf_value_with(vocabulary, generator, rdf_direction)
314				.map(|compound_value| CompoundValue {
315					value: compound_value.value,
316					triples: compound_value.triples.map(CompoundValueTriples::literal),
317				}),
318			Self::Node(node) => node.rdf_value().map(|value| CompoundValue {
319				value,
320				triples: None,
321			}),
322			Self::List(list) => {
323				if list.is_empty() {
324					Some(CompoundValue {
325						value: Value::Id(ValidId::Iri(vocabulary.insert(RDF_NIL))),
326						triples: None,
327					})
328				} else {
329					let id = generator.next(vocabulary);
330					Some(CompoundValue {
331						value: Clone::clone(&id).into_term(),
332						triples: Some(CompoundValueTriples::List(ListTriples::new(
333							list.as_slice(),
334							id,
335						))),
336					})
337				}
338			}
339		}
340	}
341}
342
343pub struct CompoundValue<'a, T, B, L> {
344	value: Value<T, B, L>,
345	triples: Option<CompoundValueTriples<'a, T, B, L>>,
346}
347
348impl<'a, T: Clone, B: Clone> crate::quad::ObjectRef<'a, T, B> {
349	pub fn rdf_value_with<V, G: Generator<V>>(
350		&self,
351		vocabulary: &mut V,
352		generator: &mut G,
353		rdf_direction: Option<RdfDirection>,
354	) -> Option<CompoundValue<'a, T, B, V::Literal>>
355	where
356		V: Vocabulary<Iri = T, BlankId = B> + IriVocabularyMut + LiteralVocabularyMut,
357	{
358		match self {
359			Self::Object(object) => object.rdf_value_with(vocabulary, generator, rdf_direction),
360			Self::Node(node) => node.rdf_value().map(|value| CompoundValue {
361				value,
362				triples: None,
363			}),
364			Self::Ref(r) => r.rdf_value().map(|value| CompoundValue {
365				value,
366				triples: None,
367			}),
368		}
369	}
370}
371
372enum ListItemTriples<'a, T, B, L> {
373	NestedList(NestedListTriples<'a, T, B>),
374	CompoundLiteral(Box<CompoundLiteralTriples<T, B, L>>),
375}
376
377struct NestedListTriples<'a, T, B> {
378	head_ref: Option<ValidId<T, B>>,
379	previous: Option<ValidId<T, B>>,
380	iter: std::slice::Iter<'a, IndexedObject<T, B>>,
381}
382
383struct ListNode<'a, 'i, T, B> {
384	id: &'i ValidId<T, B>,
385	object: &'a Indexed<Object<T, B>>,
386}
387
388impl<'a, T, B> NestedListTriples<'a, T, B> {
389	fn new(list: &'a [IndexedObject<T, B>], head_ref: ValidId<T, B>) -> Self {
390		Self {
391			head_ref: Some(head_ref),
392			previous: None,
393			iter: list.iter(),
394		}
395	}
396
397	fn previous(&self) -> Option<&ValidId<T, B>> {
398		self.previous.as_ref()
399	}
400
401	/// Pull the next object of the list.
402	///
403	/// Uses the given generator to assign as id to the list element.
404	fn next<V: Vocabulary<Iri = T, BlankId = B>, G: Generator<V>>(
405		&mut self,
406		vocabulary: &mut V,
407		generator: &mut G,
408	) -> Option<ListNode<'a, '_, T, B>> {
409		if let Some(next) = self.iter.next() {
410			let id = match self.head_ref.take() {
411				Some(id) => id,
412				None => generator.next(vocabulary),
413			};
414
415			self.previous = Some(id);
416			Some(ListNode {
417				object: next,
418				id: self.previous.as_ref().unwrap(),
419			})
420		} else {
421			None
422		}
423	}
424}
425
426pub enum CompoundValueTriples<'a, T, B, L> {
427	Literal(Box<CompoundLiteralTriples<T, B, L>>),
428	List(ListTriples<'a, T, B, L>),
429}
430
431impl<'a, T, B, L> CompoundValueTriples<'a, T, B, L> {
432	pub fn literal(l: CompoundLiteralTriples<T, B, L>) -> Self {
433		Self::Literal(Box::new(l))
434	}
435
436	pub fn with<'n, V: Vocabulary<Iri = T, BlankId = B, Literal = L>, G: Generator<V>>(
437		self,
438		vocabulary: &'n mut V,
439		generator: G,
440		rdf_direction: Option<RdfDirection>,
441	) -> CompoundValueTriplesWith<'a, 'n, V, G> {
442		CompoundValueTriplesWith {
443			vocabulary,
444			generator,
445			rdf_direction,
446			inner: self,
447		}
448	}
449
450	pub fn next<V, G: Generator<V>>(
451		&mut self,
452		vocabulary: &mut V,
453		generator: &mut G,
454		rdf_direction: Option<RdfDirection>,
455	) -> Option<Triple<T, B, L>>
456	where
457		T: Clone,
458		B: Clone,
459		L: Clone,
460		V: Vocabulary<Iri = T, BlankId = B, Literal = L> + IriVocabularyMut + LiteralVocabularyMut,
461	{
462		match self {
463			Self::Literal(l) => l.next(vocabulary),
464			Self::List(l) => l.next(vocabulary, generator, rdf_direction),
465		}
466	}
467}
468
469pub struct CompoundValueTriplesWith<'a, 'n, N: Vocabulary, G: Generator<N>> {
470	vocabulary: &'n mut N,
471	generator: G,
472	rdf_direction: Option<RdfDirection>,
473	inner: CompoundValueTriples<'a, N::Iri, N::BlankId, N::Literal>,
474}
475
476impl<'a, 'n, N: Vocabulary + IriVocabularyMut, G: Generator<N>> Iterator
477	for CompoundValueTriplesWith<'a, 'n, N, G>
478where
479	N::Iri: AsRef<Iri> + Clone,
480	N::BlankId: Clone,
481	N::Literal: Clone,
482	N: LiteralVocabularyMut,
483{
484	type Item = Triple<N::Iri, N::BlankId, N::Literal>;
485
486	fn next(&mut self) -> Option<Self::Item> {
487		self.inner
488			.next(self.vocabulary, &mut self.generator, self.rdf_direction)
489	}
490}
491
492/// Iterator over the RDF quads generated from a list of JSON-LD objects.
493///
494/// If the list contains nested lists, the iterator will also emit quads for those nested lists.
495pub struct ListTriples<'a, T, B, L> {
496	stack: SmallVec<[ListItemTriples<'a, T, B, L>; 2]>,
497	pending: Option<Triple<T, B, L>>,
498}
499
500impl<'a, T, B, L> ListTriples<'a, T, B, L> {
501	pub fn new(list: &'a [IndexedObject<T, B>], head_ref: ValidId<T, B>) -> Self {
502		let mut stack = SmallVec::new();
503		stack.push(ListItemTriples::NestedList(NestedListTriples::new(
504			list, head_ref,
505		)));
506
507		Self {
508			stack,
509			pending: None,
510		}
511	}
512
513	pub fn with<'n, V: Vocabulary<Iri = T, BlankId = B, Literal = L>, G: Generator<V>>(
514		self,
515		vocabulary: &'n mut V,
516		generator: G,
517		rdf_direction: Option<RdfDirection>,
518	) -> ListTriplesWith<'a, 'n, V, G> {
519		ListTriplesWith {
520			vocabulary,
521			generator,
522			rdf_direction,
523			inner: self,
524		}
525	}
526
527	pub fn next<V, G: Generator<V>>(
528		&mut self,
529		vocabulary: &mut V,
530		generator: &mut G,
531		rdf_direction: Option<RdfDirection>,
532	) -> Option<Triple<T, B, L>>
533	where
534		T: Clone,
535		B: Clone,
536		L: Clone,
537		V: Vocabulary<Iri = T, BlankId = B, Literal = L> + IriVocabularyMut + LiteralVocabularyMut,
538	{
539		loop {
540			if let Some(pending) = self.pending.take() {
541				break Some(pending);
542			}
543
544			match self.stack.last_mut() {
545				Some(ListItemTriples::CompoundLiteral(lit)) => match lit.next(vocabulary) {
546					Some(triple) => break Some(triple),
547					None => {
548						self.stack.pop();
549					}
550				},
551				Some(ListItemTriples::NestedList(list)) => {
552					let previous = list.previous().cloned();
553					match list.next(vocabulary, generator) {
554						Some(node) => {
555							if let Some(compound_value) =
556								node.object
557									.rdf_value_with(vocabulary, generator, rdf_direction)
558							{
559								let id = node.id.clone();
560
561								if let Some(compound_triples) = compound_value.triples {
562									match compound_triples {
563										CompoundValueTriples::List(list) => {
564											self.stack.extend(list.stack)
565										}
566										CompoundValueTriples::Literal(lit) => {
567											self.stack.push(ListItemTriples::CompoundLiteral(lit))
568										}
569									}
570								}
571
572								self.pending = Some(rdf_types::Triple(
573									id.clone(),
574									ValidId::Iri(vocabulary.insert(RDF_FIRST)),
575									compound_value.value,
576								));
577
578								if let Some(previous_id) = previous {
579									break Some(rdf_types::Triple(
580										previous_id,
581										ValidId::Iri(vocabulary.insert(RDF_REST)),
582										id.into_term(),
583									));
584								}
585							}
586						}
587						None => {
588							self.stack.pop();
589							if let Some(previous_id) = previous {
590								break Some(rdf_types::Triple(
591									previous_id,
592									ValidId::Iri(vocabulary.insert(RDF_REST)),
593									Value::Id(ValidId::Iri(vocabulary.insert(RDF_NIL))),
594								));
595							}
596						}
597					}
598				}
599				None => break None,
600			}
601		}
602	}
603}
604
605pub struct ListTriplesWith<'a, 'n, V: Vocabulary, G: Generator<V>> {
606	vocabulary: &'n mut V,
607	generator: G,
608	rdf_direction: Option<RdfDirection>,
609	inner: ListTriples<'a, V::Iri, V::BlankId, V::Literal>,
610}
611
612impl<'a, 'n, N: Vocabulary + IriVocabularyMut, G: Generator<N>> Iterator
613	for ListTriplesWith<'a, 'n, N, G>
614where
615	N::Iri: AsRef<Iri> + Clone,
616	N::BlankId: Clone,
617	N::Literal: Clone,
618	N: LiteralVocabularyMut,
619{
620	type Item = Triple<N::Iri, N::BlankId, N::Literal>;
621
622	fn next(&mut self) -> Option<Self::Item> {
623		self.inner
624			.next(self.vocabulary, &mut self.generator, self.rdf_direction)
625	}
626}
627
628fn i18n(language: Option<LangTagBuf>, direction: Direction) -> IriBuf {
629	let iri = match &language {
630		Some(language) => format!("https://www.w3.org/ns/i18n#{language}_{direction}"),
631		None => format!("https://www.w3.org/ns/i18n#{direction}"),
632	};
633
634	IriBuf::new(iri).unwrap()
635}
636
637pub type Value<T, B, L> = rdf_types::Object<ValidId<T, B>, L>;