linked_data_next/
lib.rs

1//! This library provides primitive traits to serialize and deserialize
2//! Linked-Data types. It is shipped with derive macros (using the `derive`
3//! feature) that can automatically implement those primitives for you.
4//!
5//! # Example
6//!
7//! ```
8//! use iref::IriBuf;
9//! use static_iref::iri;
10//!
11//! #[derive(linked_data_next::Serialize, linked_data_next::Deserialize)]
12//! #[ld(prefix("ex" = "http://example.org/"))]
13//! struct Foo {
14//!   #[ld(id)]
15//!   id: IriBuf,
16//!
17//!   #[ld("ex:name")]
18//!   name: String,
19//!
20//!   #[ld("ex:email")]
21//!   email: String
22//! }
23//!
24//! let value = Foo {
25//!   id: iri!("http://example.org/JohnSmith").to_owned(),
26//!   name: "John Smith".to_owned(),
27//!   email: "john.smith@example.org".to_owned()
28//! };
29//!
30//! let quads = linked_data_next::to_quads(rdf_types::generator::Blank::new(), &value)
31//!   .expect("RDF serialization failed");
32//!
33//! for quad in quads {
34//!   use rdf_types::RdfDisplay;
35//!   println!("{} .", quad.rdf_display())
36//! }
37//! ```
38//!
39//! This should print the following:
40//! ```text
41//! <http://example.org/JohnSmith> <http://example.org/name> "John Smith" .
42//! <http://example.org/JohnSmith> <http://example.org/email> "john.smith@example.org" .
43//! ```
44use educe::Educe;
45use iref::{Iri, IriBuf};
46#[cfg(feature = "derive")]
47pub use linked_data_next_derive::{Deserialize, Serialize};
48use rdf_types::{
49	dataset::{PatternMatchingDataset, TraversableDataset},
50	interpretation::ReverseIriInterpretation,
51	vocabulary::IriVocabulary,
52	Interpretation, Vocabulary,
53};
54
55#[doc(hidden)]
56pub use iref;
57
58#[doc(hidden)]
59pub use rdf_types;
60
61#[doc(hidden)]
62pub use xsd_types;
63
64#[doc(hidden)]
65pub use json_syntax;
66
67mod anonymous;
68mod datatypes;
69mod graph;
70mod r#impl;
71mod macros;
72mod predicate;
73mod quads;
74mod rdf;
75mod reference;
76mod resource;
77mod subject;
78
79pub use anonymous::*;
80pub use graph::*;
81pub use predicate::*;
82pub use quads::{
83	to_interpreted_graph_quads, to_interpreted_quads, to_interpreted_subject_quads,
84	to_lexical_quads, to_lexical_quads_with, to_lexical_subject_quads,
85	to_lexical_subject_quads_with, to_quads, to_quads_with, IntoQuadsError,
86};
87pub use rdf::*;
88pub use reference::*;
89pub use resource::*;
90pub use subject::*;
91
92#[derive(Debug, thiserror::Error)]
93pub enum FromLinkedDataError {
94	/// Resource has no IRI representation.
95	#[error("expected IRI")]
96	ExpectedIri(ContextIris),
97
98	#[error("unsupported IRI `{found}`")]
99	UnsupportedIri {
100		/// Error context.
101		context: ContextIris,
102
103		/// Unsupported IRI.
104		found: IriBuf,
105
106		/// Optional hint listing the supported IRIs.
107		supported: Option<Vec<IriBuf>>,
108	},
109
110	/// Resource has no literal representation.
111	#[error("expected literal")]
112	ExpectedLiteral(ContextIris),
113
114	/// Resource has literal representations, but none of the expected type.
115	#[error("literal type mismatch")]
116	LiteralTypeMismatch {
117		context: ContextIris,
118		expected: Option<IriBuf>,
119		found: IriBuf,
120	},
121
122	/// Resource has a literal representation of the correct type, but the
123	/// lexical value could not be successfully parsed.
124	#[error("invalid literal")]
125	InvalidLiteral(ContextIris),
126
127	/// Missing required value.
128	#[error("missing required value")]
129	MissingRequiredValue(ContextIris),
130
131	/// Too many values.
132	#[error("too many values")]
133	TooManyValues(ContextIris),
134
135	/// Generic error for invalid subjects.
136	#[error("invalid subject")]
137	InvalidSubject {
138		context: ContextIris,
139		subject: Option<IriBuf>,
140	},
141}
142
143impl FromLinkedDataError {
144	pub fn context(&self) -> &ContextIris {
145		match self {
146			Self::ExpectedIri(c) => c,
147			Self::UnsupportedIri { context, .. } => context,
148			Self::ExpectedLiteral(c) => c,
149			Self::LiteralTypeMismatch { context, .. } => context,
150			Self::InvalidLiteral(c) => c,
151			Self::MissingRequiredValue(c) => c,
152			Self::TooManyValues(c) => c,
153			Self::InvalidSubject { context, .. } => context,
154		}
155	}
156}
157
158/// Linked-Data type.
159///
160/// A Linked-Data type represents an RDF dataset which can be visited using the
161/// [`visit`](Self::visit) method.
162pub trait LinkedData<I: Interpretation = (), V: Vocabulary = ()> {
163	/// Visit the RDF dataset represented by this type.
164	fn visit<S>(&self, visitor: S) -> Result<S::Ok, S::Error>
165	where
166		S: Visitor<I, V>;
167}
168
169impl<I: Interpretation, V: Vocabulary, T: ?Sized + LinkedData<I, V>> LinkedData<I, V> for &T {
170	fn visit<S>(&self, visitor: S) -> Result<S::Ok, S::Error>
171	where
172		S: Visitor<I, V>,
173	{
174		T::visit(self, visitor)
175	}
176}
177
178impl<I: Interpretation, V: Vocabulary, T: ?Sized + LinkedData<I, V>> LinkedData<I, V> for Box<T> {
179	fn visit<S>(&self, visitor: S) -> Result<S::Ok, S::Error>
180	where
181		S: Visitor<I, V>,
182	{
183		T::visit(self, visitor)
184	}
185}
186
187impl<I: Interpretation, V: Vocabulary> LinkedData<I, V> for Iri {
188	fn visit<S>(&self, visitor: S) -> Result<S::Ok, S::Error>
189	where
190		S: Visitor<I, V>,
191	{
192		visitor.end()
193	}
194}
195
196/// RDF dataset visitor.
197pub trait Visitor<I: Interpretation = (), V: Vocabulary = ()> {
198	/// Type of the value returned by the visitor when the dataset has been
199	/// entirely visited.
200	type Ok;
201
202	/// Error type.
203	type Error;
204
205	/// Visits the default graph of the dataset.
206	fn default_graph<T>(&mut self, value: &T) -> Result<(), Self::Error>
207	where
208		T: ?Sized + LinkedDataGraph<I, V>;
209
210	/// Visits a named graph of the dataset.
211	fn named_graph<T>(&mut self, value: &T) -> Result<(), Self::Error>
212	where
213		T: ?Sized + LinkedDataResource<I, V> + LinkedDataGraph<I, V>;
214
215	/// Ends the dataset visit.
216	fn end(self) -> Result<Self::Ok, Self::Error>;
217}
218
219/// Any mutable reference to a visitor is itself a visitor.
220impl<I: Interpretation, V: Vocabulary, S: Visitor<I, V>> Visitor<I, V> for &mut S {
221	type Ok = ();
222	type Error = S::Error;
223
224	fn default_graph<T>(&mut self, value: &T) -> Result<(), Self::Error>
225	where
226		T: ?Sized + LinkedDataGraph<I, V>,
227	{
228		S::default_graph(self, value)
229	}
230
231	fn named_graph<T>(&mut self, value: &T) -> Result<(), Self::Error>
232	where
233		T: ?Sized + LinkedDataResource<I, V> + LinkedDataGraph<I, V>,
234	{
235		S::named_graph(self, value)
236	}
237
238	fn end(self) -> Result<Self::Ok, Self::Error> {
239		Ok(())
240	}
241}
242
243#[derive(Educe)]
244#[educe(Debug(bound = "I::Resource: core::fmt::Debug"), Clone, Copy)]
245pub enum ResourceOrIriRef<'a, I: Interpretation> {
246	Resource(&'a I::Resource),
247	Iri(&'a Iri),
248	Anonymous,
249}
250
251impl<I: Interpretation> ResourceOrIriRef<'_, I> {
252	pub fn into_iri<V>(self, vocabulary: &V, interpretation: &I) -> Option<IriBuf>
253	where
254		V: IriVocabulary,
255		I: ReverseIriInterpretation<Iri = V::Iri>,
256	{
257		match self {
258			Self::Resource(r) => interpretation
259				.iris_of(r)
260				.next()
261				.map(|i| vocabulary.iri(i).unwrap().to_owned()),
262			Self::Iri(i) => Some(i.to_owned()),
263			Self::Anonymous => None,
264		}
265	}
266}
267
268#[derive(Educe)]
269#[educe(Debug(bound = "I::Resource: core::fmt::Debug"), Clone, Copy)]
270pub enum Context<'a, I: Interpretation> {
271	Subject,
272	Predicate {
273		subject: ResourceOrIriRef<'a, I>,
274	},
275	Object {
276		subject: ResourceOrIriRef<'a, I>,
277		predicate: ResourceOrIriRef<'a, I>,
278	},
279}
280
281impl<'a, I: Interpretation> Context<'a, I> {
282	pub fn with_subject(self, subject: &'a I::Resource) -> Self {
283		Self::Predicate {
284			subject: ResourceOrIriRef::Resource(subject),
285		}
286	}
287
288	pub fn with_predicate(self, predicate: &'a I::Resource) -> Self {
289		match self {
290			Self::Predicate { subject } => Self::Object {
291				subject,
292				predicate: ResourceOrIriRef::Resource(predicate),
293			},
294			_ => Self::Subject,
295		}
296	}
297
298	pub fn with_predicate_iri(self, predicate: &'a Iri) -> Self {
299		match self {
300			Self::Predicate { subject } => Self::Object {
301				subject,
302				predicate: ResourceOrIriRef::Iri(predicate),
303			},
304			_ => Self::Subject,
305		}
306	}
307
308	pub fn with_anonymous_predicate(self) -> Self {
309		match self {
310			Self::Predicate { subject } => Self::Object {
311				subject,
312				predicate: ResourceOrIriRef::Anonymous,
313			},
314			_ => Self::Subject,
315		}
316	}
317
318	pub fn into_iris<V>(self, vocabulary: &V, interpretation: &I) -> ContextIris
319	where
320		V: IriVocabulary,
321		I: ReverseIriInterpretation<Iri = V::Iri>,
322	{
323		match self {
324			Self::Subject => ContextIris::Subject,
325			Self::Predicate { subject } => ContextIris::Predicate {
326				subject: subject.into_iri(vocabulary, interpretation),
327			},
328			Self::Object { subject, predicate } => ContextIris::Object {
329				subject: subject.into_iri(vocabulary, interpretation),
330				predicate: predicate.into_iri(vocabulary, interpretation),
331			},
332		}
333	}
334}
335
336#[derive(Debug, Clone)]
337pub enum ContextIris {
338	Subject,
339	Predicate {
340		subject: Option<IriBuf>,
341	},
342	Object {
343		subject: Option<IriBuf>,
344		predicate: Option<IriBuf>,
345	},
346}
347
348impl<I: Interpretation> Default for Context<'_, I> {
349	fn default() -> Self {
350		Self::Subject
351	}
352}
353
354pub trait LinkedDataDeserialize<V: Vocabulary = (), I: Interpretation = ()>: Sized {
355	fn deserialize_dataset_in(
356		vocabulary: &V,
357		interpretation: &I,
358		dataset: &(impl TraversableDataset<Resource = I::Resource> + PatternMatchingDataset),
359		context: Context<I>,
360	) -> Result<Self, FromLinkedDataError>;
361
362	fn deserialize_dataset(
363		vocabulary: &V,
364		interpretation: &I,
365		dataset: &(impl TraversableDataset<Resource = I::Resource> + PatternMatchingDataset),
366	) -> Result<Self, FromLinkedDataError> {
367		Self::deserialize_dataset_in(vocabulary, interpretation, dataset, Context::default())
368	}
369}