json_ld_context_processing/algorithm/
iri.rs1use std::hash::Hash;
2
3use super::{DefinedTerms, Environment, Merged};
4use crate::{Error, Options, ProcessingStack, Warning, WarningHandler};
5use contextual::WithContext;
6use iref::{Iri, IriRef};
7use json_ld_core::{warning, Context, Id, Loader, Term};
8use json_ld_syntax::{self as syntax, context::definition::Key, ExpandableRef, Nullable};
9use rdf_types::{
10 vocabulary::{BlankIdVocabulary, IriVocabulary},
11 BlankId, Vocabulary, VocabularyMut,
12};
13use syntax::{context::definition::KeyOrKeywordRef, is_keyword_like, CompactIri};
14
15pub struct MalformedIri(pub String);
16
17impl From<MalformedIri> for Warning {
18 fn from(MalformedIri(s): MalformedIri) -> Self {
19 Self::MalformedIri(s)
20 }
21}
22
23pub type ExpandIriResult<T, B> = Result<Option<Term<T, B>>, Error>;
25
26#[allow(clippy::too_many_arguments)]
28pub async fn expand_iri_with<'a, N, L, W>(
29 mut env: Environment<'a, N, L, W>,
30 active_context: &'a mut Context<N::Iri, N::BlankId>,
31 value: Nullable<ExpandableRef<'a>>,
32 document_relative: bool,
33 vocab: Option<Action>,
34 local_context: &'a Merged<'a>,
35 defined: &'a mut DefinedTerms,
36 remote_contexts: ProcessingStack<N::Iri>,
37 options: Options,
38) -> ExpandIriResult<N::Iri, N::BlankId>
39where
40 N: VocabularyMut,
41 N::Iri: Clone + Eq + Hash,
42 N::BlankId: Clone + PartialEq,
43 L: Loader,
44 W: WarningHandler<N>,
45{
46 match value {
47 Nullable::Null => Ok(Some(Term::Null)),
48 Nullable::Some(ExpandableRef::Keyword(k)) => Ok(Some(Term::Keyword(k))),
49 Nullable::Some(ExpandableRef::String(value)) => {
50 if is_keyword_like(value) {
51 return Ok(Some(Term::Null));
52 }
53
54 Box::pin(super::define(
60 Environment {
61 vocabulary: env.vocabulary,
62 loader: env.loader,
63 warnings: env.warnings,
64 },
65 active_context,
66 local_context,
67 value.into(),
68 defined,
69 remote_contexts.clone(),
70 None,
71 false,
72 options.with_no_override(),
73 ))
74 .await?;
75
76 if let Some(term_definition) = active_context.get(value) {
77 if let Some(value) = term_definition.value() {
80 if value.is_keyword() {
81 return Ok(Some(value.clone()));
82 }
83 }
84
85 if vocab.is_some() {
88 return match term_definition.value() {
89 Some(value) => Ok(Some(value.clone())),
90 None => Ok(Some(Term::Null)),
91 };
92 }
93 }
94
95 if value.find(':').map(|i| i > 0).unwrap_or(false) {
96 if let Ok(blank_id) = BlankId::new(value) {
97 return Ok(Some(Term::Id(Id::blank(
98 env.vocabulary.insert_blank_id(blank_id),
99 ))));
100 }
101
102 if value == "_:" {
103 return Ok(Some(Term::Id(Id::Invalid("_:".to_string()))));
104 }
105
106 if let Ok(compact_iri) = CompactIri::new(value) {
107 Box::pin(super::define(
113 Environment {
114 vocabulary: env.vocabulary,
115 loader: env.loader,
116 warnings: env.warnings,
117 },
118 active_context,
119 local_context,
120 KeyOrKeywordRef::Key(compact_iri.prefix().into()),
121 defined,
122 remote_contexts,
123 None,
124 false,
125 options.with_no_override(),
126 ))
127 .await?;
128
129 let prefix_key = Key::from(compact_iri.prefix().to_string());
133 if let Some(term_definition) = active_context.get_normal(&prefix_key) {
134 if term_definition.prefix {
135 if let Some(mapping) = &term_definition.value {
136 let mut result =
137 mapping.with(&*env.vocabulary).as_str().to_string();
138 result.push_str(compact_iri.suffix());
139
140 return Ok(Some(Term::Id(Id::from_string_in(
141 env.vocabulary,
142 result,
143 ))));
144 }
145 }
146 }
147 }
148
149 if let Ok(iri) = Iri::new(value) {
150 return Ok(Some(Term::Id(Id::iri(env.vocabulary.insert(iri)))));
151 }
152 }
153
154 if let Some(action) = vocab {
157 match active_context.vocabulary() {
158 Some(Term::Id(mapping)) => {
159 return match action {
160 Action::Keep => {
161 let mut result =
162 mapping.with(&*env.vocabulary).as_str().to_string();
163 result.push_str(value);
164
165 Ok(Some(Term::Id(Id::from_string_in(env.vocabulary, result))))
166 }
167 Action::Drop => Ok(None),
168 Action::Reject => Err(Error::ForbiddenVocab),
169 }
170 }
171 Some(_) => return Ok(Some(invalid_iri(&mut env, value.to_string()))),
172 None => (),
173 }
174 }
175
176 if document_relative {
183 if let Ok(iri_ref) = IriRef::new(value) {
184 if let Some(iri) =
185 super::resolve_iri(env.vocabulary, iri_ref, active_context.base_iri())
186 {
187 return Ok(Some(Term::from(iri)));
188 }
189 }
190 }
191
192 Ok(Some(invalid_iri(&mut env, value.to_string())))
194 }
195 }
196}
197
198fn invalid_iri<N, L, W: json_ld_core::warning::Handler<N, Warning>>(
199 env: &mut Environment<N, L, W>,
200 value: String,
201) -> Term<N::Iri, N::BlankId>
202where
203 N: Vocabulary,
204{
205 env.warnings
206 .handle(env.vocabulary, MalformedIri(value.clone()).into());
207 Term::Id(Id::Invalid(value))
208}
209
210#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
211pub enum Action {
212 #[default]
213 Keep,
214 Drop,
215 Reject,
216}
217
218impl Action {
219 pub fn is_reject(&self) -> bool {
220 matches!(self, Self::Reject)
221 }
222}
223
224#[derive(Debug)]
225pub struct RejectVocab;
226
227pub type IriExpansionResult<N> =
228 Result<Option<Term<<N as IriVocabulary>::Iri, <N as BlankIdVocabulary>::BlankId>>, RejectVocab>;
229
230pub fn expand_iri_simple<W, N, L, H>(
232 env: &mut Environment<N, L, H>,
233 active_context: &Context<N::Iri, N::BlankId>,
234 value: Nullable<ExpandableRef>,
235 document_relative: bool,
236 vocab: Option<Action>,
237) -> IriExpansionResult<N>
238where
239 N: VocabularyMut,
240 N::Iri: Clone,
241 N::BlankId: Clone,
242 W: From<MalformedIri>,
243 H: warning::Handler<N, W>,
244{
245 match value {
246 Nullable::Null => Ok(Some(Term::Null)),
247 Nullable::Some(ExpandableRef::Keyword(k)) => Ok(Some(Term::Keyword(k))),
248 Nullable::Some(ExpandableRef::String(value)) => {
249 if is_keyword_like(value) {
250 return Ok(Some(Term::Null));
251 }
252
253 if let Some(term_definition) = active_context.get(value) {
254 if let Some(value) = term_definition.value() {
257 if value.is_keyword() {
258 return Ok(Some(value.clone()));
259 }
260 }
261
262 if vocab.is_some() {
265 return match term_definition.value() {
266 Some(value) => Ok(Some(value.clone())),
267 None => Ok(Some(Term::Null)),
268 };
269 }
270 }
271
272 if value.find(':').map(|i| i > 0).unwrap_or(false) {
273 if let Ok(blank_id) = BlankId::new(value) {
274 return Ok(Some(Term::Id(Id::blank(
275 env.vocabulary.insert_blank_id(blank_id),
276 ))));
277 }
278
279 if value == "_:" {
280 return Ok(Some(Term::Id(Id::Invalid("_:".to_string()))));
281 }
282
283 if let Ok(compact_iri) = CompactIri::new(value) {
284 let prefix_key = Key::from(compact_iri.prefix().to_string());
288 if let Some(term_definition) = active_context.get_normal(&prefix_key) {
289 if term_definition.prefix {
290 if let Some(mapping) = &term_definition.value {
291 let mut result =
292 mapping.with(&*env.vocabulary).as_str().to_string();
293 result.push_str(compact_iri.suffix());
294
295 return Ok(Some(Term::Id(Id::from_string_in(
296 env.vocabulary,
297 result,
298 ))));
299 }
300 }
301 }
302 }
303
304 if let Ok(iri) = Iri::new(value) {
305 return Ok(Some(Term::Id(Id::iri(env.vocabulary.insert(iri)))));
306 }
307 }
308
309 if let Some(action) = vocab {
312 match active_context.vocabulary() {
313 Some(Term::Id(mapping)) => {
314 return match action {
315 Action::Keep => {
316 let mut result =
317 mapping.with(&*env.vocabulary).as_str().to_string();
318 result.push_str(value);
319
320 Ok(Some(Term::Id(Id::from_string_in(env.vocabulary, result))))
321 }
322 Action::Drop => Ok(None),
323 Action::Reject => Err(RejectVocab),
324 }
325 }
326 Some(_) => return Ok(Some(invalid_iri_simple(env, value.to_string()))),
327 None => (),
328 }
329 }
330
331 if document_relative {
338 if let Ok(iri_ref) = IriRef::new(value) {
339 if let Some(iri) =
340 super::resolve_iri(env.vocabulary, iri_ref, active_context.base_iri())
341 {
342 return Ok(Some(Term::from(iri)));
343 }
344 }
345 }
346
347 Ok(Some(invalid_iri_simple(env, value.to_string())))
349 }
350 }
351}
352
353fn invalid_iri_simple<W, N, L, H>(
354 env: &mut Environment<N, L, H>,
355 value: String,
356) -> Term<N::Iri, N::BlankId>
357where
358 N: Vocabulary,
359 W: From<MalformedIri>,
360 H: warning::Handler<N, W>,
361{
362 env.warnings
363 .handle(env.vocabulary, MalformedIri(value.clone()).into());
364 Term::Id(Id::Invalid(value))
365}