json_ld/algorithms/context_processing/
iri.rs1use rdf_syntax::BlankId;
2use rdf_syntax::{Iri, IriBuf, IriRef};
3
4use crate::{
5 JsonLdBacktraceBuilder, JsonLdLenient, JsonLdLocatedError, JsonLdNullable, JsonLdTerm,
6 JsonLdWarning,
7 algorithms::{
8 AsyncJsonLdEnvironment,
9 context_processing::{ContextProcessor, TargetProcessedContext, merged::Merged},
10 },
11 context::RawJsonLdProcessedContext,
12 syntax::{
13 JsonLdCompactIri, JsonLdExpandableRef,
14 context::{JsonLdContextTerm, JsonLdKeyOrKeywordRef},
15 is_keyword_like,
16 },
17};
18
19pub fn resolve_iri(iri_ref: &IriRef, base_iri: Option<&Iri>) -> Option<IriBuf> {
21 match base_iri {
22 Some(base_iri) => Some(iri_ref.resolved(base_iri)),
23 None => iri_ref.as_iri().map(ToOwned::to_owned),
24 }
25}
26
27pub type ExpandIriResult = Result<JsonLdTerm, JsonLdLocatedError>;
29
30impl<'a> ContextProcessor<'a> {
31 pub async fn expand_iri_recursive(
34 &self,
35 env: &impl AsyncJsonLdEnvironment,
36 result: &mut TargetProcessedContext<'_>,
37 local_context: &Merged<'_>,
38 value: JsonLdNullable<JsonLdExpandableRef<'_>>,
39 context_loc: JsonLdBacktraceBuilder<'_>,
40 ) -> ExpandIriResult {
41 match value {
42 JsonLdNullable::Null => Ok(JsonLdTerm::Null),
43 JsonLdNullable::Some(JsonLdExpandableRef::Keyword(k)) => Ok(JsonLdTerm::Keyword(k)),
44 JsonLdNullable::Some(JsonLdExpandableRef::String(value)) => {
45 if is_keyword_like(value) {
46 return Ok(JsonLdTerm::Null);
47 }
48
49 Box::pin(self.for_recursive_definition().define(
55 env,
56 result,
57 local_context,
58 value.into(),
59 false,
60 context_loc,
61 ))
62 .await?;
63
64 if let Some(term_definition) = result.value.get(value) {
65 if let Some(value) = term_definition.value()
68 && value.is_keyword()
69 {
70 return Ok(value.clone());
71 }
72
73 return match term_definition.value() {
75 Some(value) => Ok(value.clone()),
76 None => Ok(JsonLdTerm::Null),
77 };
78 }
79
80 if value.find(':').map(|i| i > 0).unwrap_or(false) {
81 if let Ok(blank_id) = BlankId::new(value) {
82 return Ok(JsonLdTerm::Id(JsonLdLenient::blank(blank_id.to_owned())));
83 }
84
85 if value == "_:" {
86 return Ok(JsonLdTerm::Id(JsonLdLenient::Invalid("_:".to_string())));
87 }
88
89 if let Ok(compact_iri) = JsonLdCompactIri::new(value) {
90 Box::pin(self.for_recursive_definition().define(
96 env,
97 result,
98 local_context,
99 JsonLdKeyOrKeywordRef::Key(compact_iri.prefix().into()),
100 false,
101 context_loc,
102 ))
103 .await?;
104
105 let prefix_key = JsonLdContextTerm::from(compact_iri.prefix().to_string());
109 if let Some(term_definition) = result.value.get_normal(&prefix_key)
110 && term_definition.prefix
111 && let Some(mapping) = &term_definition.value
112 {
113 let mut result = mapping.as_str().to_owned();
114 result.push_str(compact_iri.suffix());
115
116 return Ok(JsonLdTerm::Id(JsonLdLenient::from_string(result).0));
117 }
118 }
119
120 if let Ok(iri) = Iri::new(value) {
121 return Ok(JsonLdTerm::Id(JsonLdLenient::iri(iri.to_owned())));
122 }
123 }
124
125 match result.value.vocabulary() {
128 Some(JsonLdTerm::Id(mapping)) => {
129 let mut result = mapping.as_str().to_owned();
130 result.push_str(value);
131
132 return Ok(JsonLdTerm::Id(JsonLdLenient::from_string(result).0));
133 }
134 Some(_) => return Ok(invalid_iri(value.to_owned(), |w| env.warn(w))),
135 None => (),
136 }
137
138 Ok(invalid_iri(value.to_owned(), |w| env.warn(w)))
140 }
141 }
142 }
143}
144
145impl RawJsonLdProcessedContext {
146 pub fn expand_iri(
147 &self,
148 value: JsonLdNullable<JsonLdExpandableRef<'_>>,
149 document_relative: bool,
150 vocab: bool,
151 ) -> JsonLdTerm {
152 self.expand_iri_with(value, document_relative, vocab, |_| {})
153 }
154
155 pub fn expand_iri_with(
156 &self,
157 value: JsonLdNullable<JsonLdExpandableRef<'_>>,
158 document_relative: bool,
159 vocab: bool,
160 on_warning: impl FnOnce(JsonLdWarning),
161 ) -> JsonLdTerm {
162 match value {
163 JsonLdNullable::Null => JsonLdTerm::Null,
164 JsonLdNullable::Some(JsonLdExpandableRef::Keyword(k)) => JsonLdTerm::Keyword(k),
165 JsonLdNullable::Some(JsonLdExpandableRef::String(value)) => {
166 if is_keyword_like(value) {
167 return JsonLdTerm::Null;
168 }
169
170 if let Some(term_definition) = self.get(value) {
171 if let Some(value) = term_definition.value()
174 && value.is_keyword()
175 {
176 return value.clone();
177 }
178
179 if vocab {
182 return match term_definition.value() {
183 Some(value) => value.clone(),
184 None => JsonLdTerm::Null,
185 };
186 }
187 }
188
189 if value.find(':').map(|i| i > 0).unwrap_or(false) {
190 if let Ok(blank_id) = BlankId::new(value) {
191 return JsonLdTerm::Id(JsonLdLenient::blank(blank_id.to_owned()));
192 }
193
194 if value == "_:" {
195 return JsonLdTerm::Id(JsonLdLenient::Invalid("_:".to_string()));
196 }
197
198 if let Ok(compact_iri) = JsonLdCompactIri::new(value) {
199 let prefix_key = JsonLdContextTerm::from(compact_iri.prefix().to_string());
203 if let Some(term_definition) = self.get_normal(&prefix_key)
204 && term_definition.prefix
205 && let Some(mapping) = &term_definition.value
206 {
207 let mut result = mapping.as_str().to_owned();
208 result.push_str(compact_iri.suffix());
209 return JsonLdTerm::Id(JsonLdLenient::from_string(result).0);
210 }
211 }
212
213 if let Ok(iri) = Iri::new(value) {
214 return JsonLdTerm::Id(JsonLdLenient::iri(iri.to_owned()));
215 }
216 }
217
218 if vocab {
221 match self.vocabulary() {
222 Some(JsonLdTerm::Id(mapping)) => {
223 let mut result = mapping.as_str().to_owned();
224 result.push_str(value);
225
226 return JsonLdTerm::Id(JsonLdLenient::from_string(result).0);
227 }
228 Some(_) => return invalid_iri(value.to_string(), on_warning),
229 None => (),
230 }
231 }
232
233 if document_relative
240 && let Ok(iri_ref) = IriRef::new(value)
241 && let Some(iri) = resolve_iri(iri_ref, self.base_iri())
242 {
243 return JsonLdTerm::from(iri);
244 }
245
246 invalid_iri(value.to_string(), on_warning)
248 }
249 }
250 }
251}
252
253fn invalid_iri(value: String, on_warning: impl FnOnce(JsonLdWarning)) -> JsonLdTerm {
254 (on_warning)(JsonLdWarning::MalformedIri(value.clone()));
255 JsonLdTerm::Id(JsonLdLenient::Invalid(value))
256}