Skip to main content

json_ld/processor/
document.rs

1use std::sync::Arc;
2
3use json_syntax::{JsonValue, tracing::JsonErrorAt};
4use rdf_syntax::Iri;
5
6use super::{
7	JsonLdCompactResult, JsonLdCompareResult, JsonLdExpandResult, JsonLdFlattenResult,
8	JsonLdOptions, JsonLdProcessor,
9};
10use crate::{
11	JsonLdBacktraceBuilder, JsonLdDocument, JsonLdLocatedError, JsonLdSourceFileRef,
12	RemoteJsonLdContext,
13	algorithms::{AsyncJsonLdEnvironment, JsonLdCompact, JsonLdExpand},
14	context::RawJsonLdProcessedContext,
15	syntax::JsonLdCompare,
16};
17
18impl JsonLdProcessor for JsonLdDocument {
19	async fn async_compare_with(
20		&self,
21		other: &Self,
22		env: impl AsyncJsonLdEnvironment,
23		options: JsonLdOptions,
24	) -> JsonLdCompareResult {
25		if self.document.compare_json_ld(&other.document) {
26			return Ok(true);
27		}
28
29		let a = JsonLdProcessor::async_expand_with(self, env.as_ref(), options.clone()).await?;
30		let b = JsonLdProcessor::async_expand_with(other, env, options).await?;
31
32		Ok(a == b)
33	}
34
35	async fn async_expand_with(
36		&self,
37		env: impl AsyncJsonLdEnvironment,
38		mut options: JsonLdOptions,
39	) -> JsonLdExpandResult {
40		// Initialize the active context.
41		let mut active_context =
42			RawJsonLdProcessedContext::new(options.base.clone().or_else(|| self.url.clone()));
43
44		// Process expand context if provided.
45		if let Some(expand_context) = options.expand_context.take() {
46			let context_document = expand_context
47				.load(env.loader())
48				.await
49				.map_err(Into::into)
50				.json_err_at(JsonLdBacktraceBuilder::Root)?;
51
52			active_context = context_document
53				.document
54				.context
55				.process_with(
56					env.as_ref(),
57					active_context.original_base_url(),
58					&active_context,
59					options.context_processing_options(),
60					JsonLdBacktraceBuilder::new()
61						.file(JsonLdSourceFileRef::original(context_document.url())),
62				)
63				.await?
64				.into_raw();
65		}
66
67		// Process context URL from the loaded document, if any.
68		if let Some(context_url) = self.context_url() {
69			active_context = RemoteJsonLdContext::iri(context_url.to_owned())
70				.load(env.loader())
71				.await
72				.map_err(Into::into)
73				.json_err_at(JsonLdBacktraceBuilder::Root)?
74				.document
75				.context
76				.process_with(
77					env.as_ref(),
78					Some(context_url),
79					&active_context,
80					options.context_processing_options(),
81					JsonLdBacktraceBuilder::new()
82						.file(JsonLdSourceFileRef::original(Some(context_url))),
83				)
84				.await?
85				.into_raw()
86		}
87
88		// Expand the document.
89		JsonLdExpand::expand_with(self, env, &active_context, options.expansion_options()).await
90	}
91
92	async fn async_compact_with(
93		&self,
94		context: RemoteJsonLdContext,
95		env: impl AsyncJsonLdEnvironment,
96		options: JsonLdOptions,
97	) -> JsonLdCompactResult {
98		let expanded_document = Arc::new(
99			JsonLdProcessor::async_expand_with(self, env.as_ref(), options.clone().unordered())
100				.await?,
101		);
102
103		compact_expanded(
104			&expanded_document,
105			self.url(),
106			env,
107			context,
108			options,
109			JsonLdBacktraceBuilder::new().file(JsonLdSourceFileRef::expanded(
110				self.url(),
111				&expanded_document,
112			)),
113		)
114		.await
115	}
116
117	async fn async_flatten_with(
118		&self,
119		context: Option<RemoteJsonLdContext>,
120		env: impl AsyncJsonLdEnvironment,
121		options: JsonLdOptions,
122	) -> JsonLdFlattenResult {
123		let expanded_input = Arc::new(
124			JsonLdProcessor::async_expand_with(self, env.as_ref(), options.clone().unordered())
125				.await?,
126		);
127
128		let generator = rdf_syntax::generator::BlankIdGenerator::new_with_prefix("b".to_string());
129		let flattened_output = expanded_input
130			.flatten(
131				generator,
132				options.ordered,
133				JsonLdBacktraceBuilder::new()
134					.file(JsonLdSourceFileRef::expanded(self.url(), &expanded_input)),
135			)
136			.map_err(|e| (*e).cast())?;
137
138		match context {
139			Some(context) => {
140				let flattened_output = Arc::new(flattened_output);
141
142				compact_expanded(
143					&flattened_output,
144					self.url(),
145					env,
146					context,
147					options,
148					JsonLdBacktraceBuilder::new().file(JsonLdSourceFileRef::flattened(
149						self.url(),
150						&flattened_output,
151					)),
152				)
153				.await
154			}
155			None => Ok(json_syntax::to_value(flattened_output).unwrap()),
156		}
157	}
158}
159
160async fn compact_expanded(
161	expanded_input: &impl JsonLdCompact,
162	url: Option<&Iri>,
163	env: impl AsyncJsonLdEnvironment,
164	context: RemoteJsonLdContext,
165	options: JsonLdOptions,
166	location: JsonLdBacktraceBuilder<'_>,
167) -> Result<JsonValue, JsonLdLocatedError> {
168	let context_base = url.or(options.base.as_deref());
169
170	let context = context
171		.load(env.loader())
172		.await
173		.map_err(Into::into)
174		.json_err_at(JsonLdBacktraceBuilder::Root)?;
175
176	let mut active_context = context
177		.document
178		.context
179		.process_with(
180			env.as_ref(),
181			context_base,
182			&RawJsonLdProcessedContext::new(None),
183			options.context_processing_options(),
184			json_syntax::tracing::JsonBacktraceBuilder::new()
185				.file(JsonLdSourceFileRef::original(context.url())),
186		)
187		.await?;
188
189	match options.base.as_ref() {
190		Some(base) => active_context.set_base_iri(Some(base.clone())),
191		None => {
192			if options.compact_to_relative && active_context.base_iri().is_none() {
193				active_context.set_base_iri(url.map(ToOwned::to_owned));
194			}
195		}
196	}
197
198	expanded_input
199		.compact_with(env, &active_context, options.compaction_options(), location)
200		.await
201}