Skip to main content

json_ld/algorithms/compaction/
mod.rs

1//! Compaction algorithm.
2//!
3//! See: <https://www.w3.org/TR/json-ld-api/#compaction-algorithms>
4mod collection;
5mod document;
6mod iri;
7mod object;
8mod options;
9
10use json_syntax::{JsonObject, JsonValue};
11pub use options::*;
12
13use crate::{
14	JsonLdBacktraceBuilder, JsonLdIndexed, JsonLdLocatedError, JsonLdProcessedContext, JsonLdTerm,
15	algorithms::AsyncJsonLdEnvironment,
16	context::{
17		RawJsonLdProcessedContext,
18		inverse::{JsonLdLangSelection, JsonLdTypeSelection},
19	},
20	syntax::JsonLdKeyword,
21};
22
23/// Document that can be compacted.
24pub trait JsonLdCompact {
25	/// Compacts the input document with the given options.
26	#[allow(async_fn_in_trait)]
27	async fn compact_with(
28		&self,
29		env: impl AsyncJsonLdEnvironment,
30		context: &JsonLdProcessedContext<'_>,
31		options: JsonLdCompactionOptions,
32		location: JsonLdBacktraceBuilder<'_>,
33	) -> Result<JsonValue, JsonLdLocatedError>;
34
35	/// Compacts the input document with the default options.
36	#[allow(async_fn_in_trait)]
37	async fn compact(
38		&self,
39		env: impl AsyncJsonLdEnvironment,
40		context: &JsonLdProcessedContext<'_>,
41		location: JsonLdBacktraceBuilder<'_>,
42	) -> Result<JsonValue, JsonLdLocatedError> {
43		self.compact_with(env, context, JsonLdCompactionOptions::default(), location)
44			.await
45	}
46}
47
48impl<T: JsonLdCompact> JsonLdCompact for std::sync::Arc<T> {
49	async fn compact_with(
50		&self,
51		env: impl AsyncJsonLdEnvironment,
52		context: &JsonLdProcessedContext<'_>,
53		options: JsonLdCompactionOptions,
54		location: JsonLdBacktraceBuilder<'_>,
55	) -> Result<JsonValue, JsonLdLocatedError> {
56		T::compact_with(self, env, context, options, location).await
57	}
58
59	async fn compact(
60		&self,
61		env: impl AsyncJsonLdEnvironment,
62		context: &JsonLdProcessedContext<'_>,
63		location: JsonLdBacktraceBuilder<'_>,
64	) -> Result<JsonValue, JsonLdLocatedError> {
65		T::compact(self, env, context, location).await
66	}
67}
68
69/// Compactor.
70struct Compactor<'a> {
71	pub options: JsonLdCompactionOptions,
72	pub active_context: &'a RawJsonLdProcessedContext,
73	pub type_scoped_context: &'a RawJsonLdProcessedContext,
74	pub active_property: Option<&'a str>,
75}
76
77impl Compactor<'_> {
78	/// Wraps a compacted top-level array into a node object, using the
79	/// (possibly aliased) `@graph` key.
80	///
81	/// An empty array becomes an empty object. Any other value is returned
82	/// unchanged. This is the last compaction step before the context is
83	/// embedded into the result.
84	fn wrap_in_graph(&self, compact: JsonValue) -> Result<JsonValue, JsonLdLocatedError> {
85		let JsonValue::Array(array) = compact else {
86			return Ok(compact);
87		};
88
89		let mut obj = JsonObject::new();
90
91		if !array.is_empty() {
92			// No meaningful source location available at this post-processing step.
93			let key = self.compact_iri(
94				&JsonLdTerm::Keyword(JsonLdKeyword::Graph),
95				true,
96				false,
97				JsonLdBacktraceBuilder::Root,
98			)?;
99
100			obj.insert(key.unwrap(), array.into());
101		}
102
103		Ok(obj.into())
104	}
105}
106
107impl<'a> Compactor<'a> {
108	pub fn new(
109		active_context: &'a RawJsonLdProcessedContext,
110		options: JsonLdCompactionOptions,
111	) -> Self {
112		Self {
113			options,
114			active_context,
115			type_scoped_context: active_context,
116			active_property: None,
117		}
118	}
119
120	pub fn with_active_context<'b>(
121		&'b self,
122		active_context: &'b RawJsonLdProcessedContext,
123	) -> Compactor<'b> {
124		Compactor {
125			options: self.options,
126			active_context,
127			type_scoped_context: self.type_scoped_context,
128			active_property: self.active_property,
129		}
130	}
131
132	pub fn with_type_scoped_context<'b>(
133		&'b self,
134		type_scoped_context: &'b RawJsonLdProcessedContext,
135	) -> Compactor<'b> {
136		Compactor {
137			options: self.options,
138			active_context: self.active_context,
139			type_scoped_context,
140			active_property: self.active_property,
141		}
142	}
143
144	pub fn with_active_property<'b>(&'b self, active_property: Option<&'b str>) -> Compactor<'b> {
145		Compactor {
146			options: self.options,
147			active_context: self.active_context,
148			type_scoped_context: self.type_scoped_context,
149			active_property,
150		}
151	}
152}
153
154trait CompactFragment {
155	#[allow(async_fn_in_trait)]
156	async fn compact_fragment(
157		&self,
158		env: &impl AsyncJsonLdEnvironment,
159		compactor: &Compactor,
160		location: JsonLdBacktraceBuilder<'_>,
161	) -> Result<JsonValue, JsonLdLocatedError>;
162}
163
164enum TypeLangValue<'a> {
165	Type(JsonLdTypeSelection),
166	Lang(JsonLdLangSelection<'a>),
167}
168
169/// Type that can be compacted with an index.
170trait CompactIndexedFragment {
171	#[allow(async_fn_in_trait)]
172	#[allow(clippy::too_many_arguments)]
173	async fn compact_indexed_fragment(
174		&self,
175		env: &impl AsyncJsonLdEnvironment,
176		compactor: &Compactor<'_>,
177		index: Option<&str>,
178		location: JsonLdBacktraceBuilder<'_>,
179	) -> Result<JsonValue, JsonLdLocatedError>;
180}
181
182impl<T: CompactIndexedFragment> CompactFragment for JsonLdIndexed<T> {
183	async fn compact_fragment(
184		&self,
185		env: &impl AsyncJsonLdEnvironment,
186		compactor: &Compactor<'_>,
187		location: JsonLdBacktraceBuilder<'_>,
188	) -> Result<JsonValue, JsonLdLocatedError> {
189		self.inner()
190			.compact_indexed_fragment(env, compactor, self.index(), location)
191			.await
192	}
193}