Skip to main content

json_ld/processor/
mod.rs

1use json_syntax::JsonValue;
2use linked_data::ser::to_rdf_quads_interpretation_with;
3use rdf_syntax::{GenInterpretation, IriBuf};
4use rdf_syntax::{Generator, Quad, Term};
5
6/// Drives a future that is expected to complete synchronously on the first
7/// poll (i.e., backed by a sync [`JsonLdEnvironment`]).
8///
9/// Uses a no-op waker so no thread is ever parked. Panics if the future
10/// returns [`Poll::Pending`], which would indicate a bug in the caller.
11fn resolve_sync<F: std::future::Future>(f: F) -> F::Output {
12	let waker = futures::task::noop_waker_ref();
13	let mut cx = std::task::Context::from_waker(waker);
14	let mut f = std::pin::pin!(f);
15	match f.as_mut().poll(&mut cx) {
16		std::task::Poll::Ready(result) => result,
17		std::task::Poll::Pending => {
18			panic!("async function backed by a sync loader returned Poll::Pending")
19		}
20	}
21}
22
23use crate::JsonLdVisit;
24use crate::{
25	JsonLdDirection, JsonLdDocument, JsonLdExpandedDocument, JsonLdLocatedError,
26	JsonLdProcessingMode, RemoteJsonLdContext,
27	algorithms::{
28		AsyncJsonLdEnvironment, JsonLdCompactionOptions, JsonLdContextProcessingOptions,
29		JsonLdEnvironment, JsonLdExpansionOptions, JsonLdExpansionPolicy, RdfSerializationOptions,
30	},
31};
32
33mod document;
34
35/// JSON-LD Processor options.
36#[derive(Clone)]
37pub struct JsonLdOptions {
38	/// The base IRI to use when expanding or compacting the document.
39	///
40	/// If set, this overrides the input document's IRI.
41	pub base: Option<IriBuf>,
42
43	/// If set to true, the JSON-LD processor replaces arrays with just one element with that element during compaction.
44	///
45	/// If set to false, all arrays will remain arrays even if they have just one element.
46	///
47	/// Defaults to `true`.
48	pub compact_arrays: bool,
49
50	/// Determines if IRIs are compacted relative to the base option or document
51	/// location when compacting.
52	///
53	/// Defaults to `true`.
54	pub compact_to_relative: bool,
55
56	/// A context that is used to initialize the active context when expanding a document.
57	pub expand_context: Option<RemoteJsonLdContext>,
58
59	/// If set to `true`, certain algorithm processing steps where indicated are
60	/// ordered lexicographically.
61	///
62	/// If `false`, order is not considered in processing.
63	///
64	/// Defaults to `false`.
65	pub ordered: bool,
66
67	/// Sets the processing mode.
68	///
69	/// Defaults to `JsonLdProcessingMode::JsonLd1_1`.
70	pub processing_mode: JsonLdProcessingMode,
71
72	/// Determines how value objects containing a base direction are transformed
73	/// to and from RDF.
74	pub rdf_direction: Option<JsonLdDirection>,
75
76	/// If set to `true`, the JSON-LD processor may emit blank nodes for triple
77	/// predicates, otherwise they will be omitted.
78	pub produce_generalized_rdf: bool,
79
80	/// Term expansion policy, passed to the document expansion algorithm.
81	pub expansion_policy: JsonLdExpansionPolicy,
82}
83
84impl JsonLdOptions {
85	/// Returns these options with the `ordered` flag set to `false`.
86	pub fn unordered(self) -> Self {
87		Self {
88			ordered: false,
89			..self
90		}
91	}
92
93	/// Returns these options with the `expand_context` set to the given
94	/// `context`.
95	pub fn with_expand_context(self, context: RemoteJsonLdContext) -> Self {
96		Self {
97			expand_context: Some(context),
98			..self
99		}
100	}
101
102	/// Builds options for the context processing algorithm from these options.
103	pub fn context_processing_options(&self) -> JsonLdContextProcessingOptions {
104		JsonLdContextProcessingOptions {
105			processing_mode: self.processing_mode,
106			..Default::default()
107		}
108	}
109
110	/// Builds options for the expansion algorithm from these options.
111	pub fn expansion_options(&self) -> JsonLdExpansionOptions {
112		JsonLdExpansionOptions {
113			processing_mode: self.processing_mode,
114			ordered: self.ordered,
115			policy: self.expansion_policy,
116		}
117	}
118
119	/// Builds options for the compaction algorithm from these options.
120	pub fn compaction_options(&self) -> JsonLdCompactionOptions {
121		JsonLdCompactionOptions {
122			processing_mode: self.processing_mode,
123			compact_to_relative: self.compact_to_relative,
124			compact_arrays: self.compact_arrays,
125			ordered: self.ordered,
126		}
127	}
128
129	pub fn rdf_serialization_options(&self) -> RdfSerializationOptions {
130		RdfSerializationOptions {
131			produce_generalized_rdf: self.produce_generalized_rdf,
132		}
133	}
134}
135
136impl Default for JsonLdOptions {
137	fn default() -> Self {
138		Self {
139			base: None,
140			compact_arrays: true,
141			compact_to_relative: true,
142			expand_context: None,
143			ordered: false,
144			processing_mode: JsonLdProcessingMode::JsonLd1_1,
145			rdf_direction: None,
146			produce_generalized_rdf: false,
147			expansion_policy: JsonLdExpansionPolicy::default(),
148		}
149	}
150}
151
152/// Result returned by the [`JsonLdProcessor::expand`] function.
153pub type JsonLdExpandResult = Result<JsonLdExpandedDocument, JsonLdLocatedError>;
154
155/// Result of converting into a [`JsonLdDocument`].
156pub type JsonLdIntoDocumentResult = Result<JsonLdDocument, JsonLdLocatedError>;
157
158/// Result of the [`JsonLdProcessor::compact`] function.
159pub type JsonLdCompactResult = Result<JsonValue, JsonLdLocatedError>;
160
161/// Result of the [`JsonLdProcessor::flatten`] function.
162pub type JsonLdFlattenResult = Result<JsonValue, JsonLdLocatedError>;
163
164pub type JsonLdToRdfResult = Result<Vec<Quad<Term>>, JsonLdLocatedError>;
165
166/// Result of the [`JsonLdProcessor::compare`] function.
167pub type JsonLdCompareResult = Result<bool, JsonLdLocatedError>;
168
169/// The `JsonLdProcessor` interface is the high-level programming structure that
170/// developers use to access the JSON-LD transformation methods.
171///
172/// It is notably implemented for the [`JsonLdDocument`] type.
173///
174/// # Methods naming
175///
176/// Each processing function is declined in two variants:
177///
178///  - `_with`: allows passing custom [`JsonLdOptions`].
179///  - no suffix: uses default options.
180pub trait JsonLdProcessor: Sized {
181	/// Compare this document against `other` using the given `options`.
182	fn compare_with(
183		&self,
184		other: &Self,
185		env: impl JsonLdEnvironment,
186		options: JsonLdOptions,
187	) -> JsonLdCompareResult {
188		resolve_sync(self.async_compare_with(other, env.into_async_environment(), options))
189	}
190
191	/// Compare this document against `other` using default options.
192	fn compare(&self, other: &Self, env: impl JsonLdEnvironment) -> JsonLdCompareResult {
193		self.compare_with(other, env, JsonLdOptions::default())
194	}
195
196	/// Compare this document against `other` using the given `options`.
197	#[allow(async_fn_in_trait)]
198	async fn async_compare_with(
199		&self,
200		other: &Self,
201		env: impl AsyncJsonLdEnvironment,
202		options: JsonLdOptions,
203	) -> JsonLdCompareResult;
204
205	/// Compare this document against `other` using default options.
206	#[allow(async_fn_in_trait)]
207	async fn async_compare(
208		&self,
209		other: &Self,
210		env: impl AsyncJsonLdEnvironment,
211	) -> JsonLdCompareResult {
212		self.async_compare_with(other, env, JsonLdOptions::default())
213			.await
214	}
215
216	/// Expand the document using the given `options`.
217	fn expand_with(
218		&self,
219		env: impl JsonLdEnvironment,
220		options: JsonLdOptions,
221	) -> JsonLdExpandResult {
222		resolve_sync(self.async_expand_with(env.into_async_environment(), options))
223	}
224
225	/// Expand the document using default options.
226	fn expand(&self, env: impl JsonLdEnvironment) -> JsonLdExpandResult {
227		self.expand_with(env, JsonLdOptions::default())
228	}
229
230	/// Expand the document using the given `options`.
231	#[allow(async_fn_in_trait)]
232	async fn async_expand_with(
233		&self,
234		env: impl AsyncJsonLdEnvironment,
235		options: JsonLdOptions,
236	) -> JsonLdExpandResult;
237
238	/// Expand the document using default options.
239	#[allow(async_fn_in_trait)]
240	async fn async_expand(&self, env: impl AsyncJsonLdEnvironment) -> JsonLdExpandResult {
241		self.async_expand_with(env, JsonLdOptions::default()).await
242	}
243
244	/// Compact the document relative to `context` using the given `options`.
245	fn compact_with(
246		&self,
247		context: RemoteJsonLdContext,
248		env: impl JsonLdEnvironment,
249		options: JsonLdOptions,
250	) -> JsonLdCompactResult {
251		resolve_sync(self.async_compact_with(context, env.into_async_environment(), options))
252	}
253
254	/// Compact the document relative to `context` using default options.
255	fn compact(
256		&self,
257		context: RemoteJsonLdContext,
258		env: impl JsonLdEnvironment,
259	) -> JsonLdCompactResult {
260		self.compact_with(context, env, JsonLdOptions::default())
261	}
262
263	/// Compact the document relative to `context` using the given `options`.
264	#[allow(async_fn_in_trait)]
265	async fn async_compact_with(
266		&self,
267		context: RemoteJsonLdContext,
268		env: impl AsyncJsonLdEnvironment,
269		options: JsonLdOptions,
270	) -> JsonLdCompactResult;
271
272	/// Compact the document relative to `context` using default options.
273	#[allow(async_fn_in_trait)]
274	async fn async_compact(
275		&self,
276		context: RemoteJsonLdContext,
277		env: impl AsyncJsonLdEnvironment,
278	) -> JsonLdCompactResult {
279		self.async_compact_with(context, env, JsonLdOptions::default())
280			.await
281	}
282
283	/// Flatten the document using the given `options`.
284	///
285	/// An optional `context` can be given to compact the result.
286	fn flatten_with(
287		&self,
288		context: Option<RemoteJsonLdContext>,
289		env: impl JsonLdEnvironment,
290		options: JsonLdOptions,
291	) -> JsonLdFlattenResult {
292		resolve_sync(self.async_flatten_with(context, env.into_async_environment(), options))
293	}
294
295	/// Flatten the document using default options.
296	fn flatten(
297		&self,
298		context: Option<RemoteJsonLdContext>,
299		env: impl JsonLdEnvironment,
300	) -> JsonLdFlattenResult {
301		self.flatten_with(context, env, JsonLdOptions::default())
302	}
303
304	/// Flatten the document using the given `options`.
305	///
306	/// An optional `context` can be given to compact the result.
307	#[allow(async_fn_in_trait)]
308	async fn async_flatten_with(
309		&self,
310		context: Option<RemoteJsonLdContext>,
311		env: impl AsyncJsonLdEnvironment,
312		options: JsonLdOptions,
313	) -> JsonLdFlattenResult;
314
315	/// Flatten the document using default options.
316	#[allow(async_fn_in_trait)]
317	async fn async_flatten(
318		&self,
319		context: Option<RemoteJsonLdContext>,
320		env: impl AsyncJsonLdEnvironment,
321	) -> JsonLdFlattenResult {
322		self.async_flatten_with(context, env, JsonLdOptions::default())
323			.await
324	}
325
326	/// Serialize the document to RDF using the given `options`.
327	fn to_rdf_with(
328		&self,
329		env: impl JsonLdEnvironment,
330		generator: impl Generator,
331		options: JsonLdOptions,
332	) -> JsonLdToRdfResult {
333		resolve_sync(self.async_to_rdf_with(env.into_async_environment(), generator, options))
334	}
335
336	/// Serialize the document to RDF using default options.
337	fn to_rdf(&self, env: impl JsonLdEnvironment, generator: impl Generator) -> JsonLdToRdfResult {
338		self.to_rdf_with(env, generator, JsonLdOptions::default())
339	}
340
341	/// Serialize the document to RDF using the given `options`.
342	#[allow(async_fn_in_trait)]
343	async fn async_to_rdf_with(
344		&self,
345		env: impl AsyncJsonLdEnvironment,
346		mut generator: impl Generator,
347		options: JsonLdOptions,
348	) -> JsonLdToRdfResult {
349		let rdf_serialization_options = options.rdf_serialization_options();
350		let mut expanded = JsonLdProcessor::async_expand_with(self, env, options).await?;
351		expanded.relabel(&mut generator);
352		let interpretation = GenInterpretation::new(generator);
353		Ok(
354			to_rdf_quads_interpretation_with(&expanded, interpretation, rdf_serialization_options)
355				.unwrap(),
356		)
357	}
358
359	/// Serialize the document to RDF using default options.
360	#[allow(async_fn_in_trait)]
361	async fn async_to_rdf(
362		&self,
363		env: impl AsyncJsonLdEnvironment,
364		generator: impl Generator,
365	) -> JsonLdToRdfResult {
366		self.async_to_rdf_with(env, generator, JsonLdOptions::default())
367			.await
368	}
369}