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
13pub trait EmbedContext {
20 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
33pub trait Compact<I, B> {
35 #[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 #[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 #[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}