json_ld_compaction_next/
document.rs

1use json_ld_core_next::{ExpandedDocument, FlattenedDocument, Loader, Term};
2use json_ld_syntax_next::{IntoJson, Keyword};
3use rdf_types::{vocabulary, Vocabulary};
4use std::hash::Hash;
5
6use crate::{
7	iri::{compact_iri, IriConfusedWithPrefix},
8	CompactFragment,
9};
10
11pub type CompactDocumentResult = Result<json_syntax::Value, crate::Error>;
12
13/// Context embeding method.
14///
15/// This trait provides the `embed_context` method that can be used
16/// to include a JSON-LD context to a JSON-LD document.
17/// It is used at the end of compaction algorithm to embed to
18/// context used to compact the document into the compacted output.
19pub trait EmbedContext {
20	/// Embeds the given context into the document.
21	fn embed_context<N>(
22		&mut self,
23		vocabulary: &N,
24		context: json_ld_context_processing_next::ProcessedRef<N::Iri, N::BlankId>,
25		options: crate::Options,
26	) -> Result<(), IriConfusedWithPrefix>
27	where
28		N: Vocabulary,
29		N::Iri: Clone + Hash + Eq,
30		N::BlankId: Clone + Hash + Eq;
31}
32
33/// Compaction function.
34pub trait Compact<I, B> {
35	/// Compacts the input document with full options.
36	#[allow(async_fn_in_trait)]
37	async fn compact_full<'a, N, L>(
38		&'a self,
39		vocabulary: &'a mut N,
40		context: json_ld_context_processing_next::ProcessedRef<'a, 'a, I, B>,
41		loader: &'a L,
42		options: crate::Options,
43	) -> CompactDocumentResult
44	where
45		N: rdf_types::VocabularyMut<Iri = I, BlankId = B>,
46		I: Clone + Hash + Eq,
47		B: Clone + Hash + Eq,
48		L: Loader;
49
50	/// Compacts the input document with the given `vocabulary` to
51	/// interpret identifiers.
52	#[allow(async_fn_in_trait)]
53	async fn compact_with<'a, N, L>(
54		&'a self,
55		vocabulary: &'a mut N,
56		context: json_ld_context_processing_next::ProcessedRef<'a, 'a, I, B>,
57		loader: &'a L,
58	) -> CompactDocumentResult
59	where
60		N: rdf_types::VocabularyMut<Iri = I, BlankId = B>,
61		I: Clone + Hash + Eq,
62		B: Clone + Hash + Eq,
63		L: Loader,
64	{
65		self.compact_full(vocabulary, context, loader, crate::Options::default())
66			.await
67	}
68
69	/// Compacts the input document.
70	#[allow(async_fn_in_trait)]
71	async fn compact<'a, L>(
72		&'a self,
73		context: json_ld_context_processing_next::ProcessedRef<'a, 'a, I, B>,
74		loader: &'a L,
75	) -> CompactDocumentResult
76	where
77		(): rdf_types::VocabularyMut<Iri = I, BlankId = B>,
78		I: Clone + Hash + Eq,
79		B: Clone + Hash + Eq,
80		L: Loader,
81	{
82		self.compact_with(vocabulary::no_vocabulary_mut(), context, loader)
83			.await
84	}
85}
86
87impl<I, B> Compact<I, B> for ExpandedDocument<I, B> {
88	async fn compact_full<'a, N, L>(
89		&'a self,
90		vocabulary: &'a mut N,
91		context: json_ld_context_processing_next::ProcessedRef<'a, 'a, I, B>,
92		loader: &'a L,
93		options: crate::Options,
94	) -> CompactDocumentResult
95	where
96		N: rdf_types::VocabularyMut<Iri = I, BlankId = B>,
97		I: Clone + Hash + Eq,
98		B: Clone + Hash + Eq,
99		L: Loader,
100	{
101		let mut compacted_output = self
102			.objects()
103			.compact_fragment_full(
104				vocabulary,
105				context.processed(),
106				context.processed(),
107				None,
108				loader,
109				options,
110			)
111			.await?;
112
113		compacted_output.embed_context(vocabulary, context, options)?;
114
115		Ok(compacted_output)
116	}
117}
118
119impl<I, B> Compact<I, B> for FlattenedDocument<I, B> {
120	async fn compact_full<'a, N, L>(
121		&'a self,
122		vocabulary: &'a mut N,
123		context: json_ld_context_processing_next::ProcessedRef<'a, 'a, I, B>,
124		loader: &'a L,
125		options: crate::Options,
126	) -> CompactDocumentResult
127	where
128		N: rdf_types::VocabularyMut<Iri = I, BlankId = B>,
129		I: Clone + Hash + Eq,
130		B: Clone + Hash + Eq,
131		L: Loader,
132	{
133		let mut compacted_output = self
134			.compact_fragment_full(
135				vocabulary,
136				context.processed(),
137				context.processed(),
138				None,
139				loader,
140				options,
141			)
142			.await?;
143
144		compacted_output.embed_context(vocabulary, context, options)?;
145
146		Ok(compacted_output)
147	}
148}
149
150impl EmbedContext for json_syntax::Value {
151	fn embed_context<N>(
152		&mut self,
153		vocabulary: &N,
154		context: json_ld_context_processing_next::ProcessedRef<N::Iri, N::BlankId>,
155		options: crate::Options,
156	) -> Result<(), IriConfusedWithPrefix>
157	where
158		N: Vocabulary,
159		N::Iri: Clone + Hash + Eq,
160		N::BlankId: Clone + Hash + Eq,
161	{
162		let value = self.take();
163
164		let obj = match value {
165			json_syntax::Value::Array(array) => {
166				let mut obj = json_syntax::Object::new();
167
168				if !array.is_empty() {
169					let key = compact_iri(
170						vocabulary,
171						context.processed(),
172						&Term::Keyword(Keyword::Graph),
173						true,
174						false,
175						options,
176					)?;
177
178					obj.insert(key.unwrap().into(), array.into());
179				}
180
181				Some(obj)
182			}
183			json_syntax::Value::Object(obj) => Some(obj),
184			_null => None,
185		};
186
187		if let Some(mut obj) = obj {
188			let json_context = IntoJson::into_json(context.unprocessed().clone());
189
190			if !obj.is_empty()
191				&& !json_context.is_null()
192				&& !json_context.is_empty_array_or_object()
193			{
194				obj.insert_front("@context".into(), json_context);
195			}
196
197			*self = obj.into()
198		};
199
200		Ok(())
201	}
202}