1use iref::{Iri, IriBuf};
2use json_ld::{
3 syntax::TryFromJson, LoadError, Loader, RemoteContext, RemoteContextReference, RemoteDocument,
4};
5use json_syntax::Parse;
6use static_iref::iri;
7use std::collections::HashMap;
8use thiserror::Error;
9
10pub const CREDENTIALS_V1_CONTEXT: &Iri = iri!("https://www.w3.org/2018/credentials/v1");
11pub const CREDENTIALS_V2_CONTEXT: &Iri = iri!("https://www.w3.org/ns/credentials/v2");
12pub const CREDENTIALS_EXAMPLES_V1_CONTEXT: &Iri =
13 iri!("https://www.w3.org/2018/credentials/examples/v1");
14pub const CREDENTIALS_EXAMPLES_V2_CONTEXT: &Iri =
15 iri!("https://www.w3.org/ns/credentials/examples/v2");
16pub const ODRL_CONTEXT: &Iri = iri!("https://www.w3.org/ns/odrl.jsonld");
17pub const SECURITY_V1_CONTEXT: &Iri = iri!("https://w3id.org/security/v1");
18pub const SECURITY_V2_CONTEXT: &Iri = iri!("https://w3id.org/security/v2");
19pub const SCHEMA_ORG_CONTEXT: &Iri = iri!("https://schema.org/");
20pub const DID_V1_CONTEXT: &Iri = iri!("https://www.w3.org/ns/did/v1");
21pub const DID_V1_CONTEXT_NO_WWW: &Iri = iri!("https://w3.org/ns/did/v1");
22pub const W3ID_DID_V1_CONTEXT: &Iri = iri!("https://w3id.org/did/v1");
23pub const DID_RESOLUTION_V1_CONTEXT: &Iri = iri!("https://w3id.org/did-resolution/v1");
24pub const DIF_ESRS2020_CONTEXT: &Iri = iri!("https://identity.foundation/EcdsaSecp256k1RecoverySignature2020/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld");
25#[deprecated(note = "Use W3ID_ESRS2020_V2_CONTEXT instead")]
26pub const ESRS2020_EXTRA_CONTEXT: &Iri =
27 iri!("https://demo.spruceid.com/EcdsaSecp256k1RecoverySignature2020/esrs2020-extra-0.0.jsonld");
28pub const W3ID_ESRS2020_V2_CONTEXT: &Iri =
29 iri!("https://w3id.org/security/suites/secp256k1recovery-2020/v2");
30pub const LDS_JWS2020_V1_CONTEXT: &Iri =
31 iri!("https://w3c-ccg.github.io/lds-jws2020/contexts/lds-jws2020-v1.json");
32pub const W3ID_JWS2020_V1_CONTEXT: &Iri = iri!("https://w3id.org/security/suites/jws-2020/v1");
33pub const W3ID_ED2020_V1_CONTEXT: &Iri = iri!("https://w3id.org/security/suites/ed25519-2020/v1");
34pub const W3ID_MULTIKEY_V1_CONTEXT: &Iri = iri!("https://w3id.org/security/multikey/v1");
35pub const W3ID_DATA_INTEGRITY_V1_CONTEXT: &Iri =
36 iri!("https://w3id.org/security/data-integrity/v1");
37pub const W3ID_DATA_INTEGRITY_V2_CONTEXT: &Iri =
38 iri!("https://w3id.org/security/data-integrity/v2");
39pub const BLOCKCHAIN2021_V1_CONTEXT: &Iri =
40 iri!("https://w3id.org/security/suites/blockchain-2021/v1");
41pub const CITIZENSHIP_V1_CONTEXT: &Iri = iri!("https://w3id.org/citizenship/v1");
42pub const VACCINATION_V1_CONTEXT: &Iri = iri!("https://w3id.org/vaccination/v1");
43pub const TRACEABILITY_CONTEXT: &Iri = iri!("https://w3id.org/traceability/v1");
44pub const REVOCATION_LIST_2020_V1_CONTEXT: &Iri =
45 iri!("https://w3id.org/vc-revocation-list-2020/v1");
46pub const BBS_V1_CONTEXT: &Iri = iri!("https://w3id.org/security/bbs/v1");
47pub const STATUS_LIST_2021_V1_CONTEXT: &Iri = iri!("https://w3id.org/vc/status-list/2021/v1");
48pub const EIP712SIG_V0_1_CONTEXT: &Iri =
49 iri!("https://demo.spruceid.com/ld/eip712sig-2021/v0.1.jsonld");
50pub const EIP712SIG_V1_CONTEXT: &Iri = iri!("https://w3id.org/security/suites/eip712sig-2021/v1");
51pub const PRESENTATION_SUBMISSION_V1_CONTEXT: &Iri =
52 iri!("https://identity.foundation/presentation-exchange/submission/v1");
53pub const VDL_V1_CONTEXT: &Iri = iri!("https://w3id.org/vdl/v1");
54pub const WALLET_V1_CONTEXT: &Iri = iri!("https://w3id.org/wallet/v1");
55pub const ZCAP_V1_CONTEXT: &Iri = iri!("https://w3id.org/zcap/v1");
56pub const CACAO_ZCAP_V1_CONTEXT: &Iri =
57 iri!("https://demo.didkit.dev/2022/cacao-zcap/contexts/v1.json");
58pub const JFF_VC_EDU_PLUGFEST_2022_CONTEXT: &Iri =
59 iri!("https://w3c-ccg.github.io/vc-ed/plugfest-1-2022/jff-vc-edu-plugfest-1-context.json");
60pub const DID_CONFIGURATION_V0_0_CONTEXT: &Iri =
61 iri!("https://identity.foundation/.well-known/contexts/did-configuration-v0.0.jsonld");
62pub const DID_CONFIGURATION_V1_CONTEXT: &Iri =
63 iri!("https://identity.foundation/.well-known/did-configuration/v1");
64pub const JFF_VC_EDU_PLUGFEST_2022_2_CONTEXT: &Iri =
65 iri!("https://purl.imsglobal.org/spec/ob/v3p0/context.json");
66pub const LINKED_VP_V1_CONTEXT: &Iri = iri!("https://identity.foundation/linked-vp/contexts/v1");
67
68fn load_static_context(iri: &Iri, content: &str) -> RemoteDocument {
70 RemoteDocument::new(
71 Some(iri.to_owned()),
72 Some("application/ld+json".parse().unwrap()),
73 json_syntax::Value::parse_str(content).unwrap().0,
74 )
75}
76
77lazy_static::lazy_static! {
78 pub static ref CREDENTIALS_V1_CONTEXT_DOCUMENT: RemoteDocument = load_static_context(
79 CREDENTIALS_V1_CONTEXT,
80 ssi_contexts::CREDENTIALS_V1
81 );
82 pub static ref CREDENTIALS_V2_CONTEXT_DOCUMENT: RemoteDocument = load_static_context(
83 CREDENTIALS_V2_CONTEXT,
84 ssi_contexts::CREDENTIALS_V2
85 );
86 pub static ref CREDENTIALS_EXAMPLES_V1_CONTEXT_DOCUMENT: RemoteDocument = load_static_context(
87 CREDENTIALS_EXAMPLES_V1_CONTEXT,
88 ssi_contexts::CREDENTIALS_EXAMPLES_V1
89 );
90 pub static ref CREDENTIALS_EXAMPLES_V2_CONTEXT_DOCUMENT: RemoteDocument = load_static_context(
91 CREDENTIALS_EXAMPLES_V2_CONTEXT,
92 ssi_contexts::CREDENTIALS_EXAMPLES_V2
93 );
94 pub static ref ODRL_CONTEXT_DOCUMENT: RemoteDocument = load_static_context(
95 ODRL_CONTEXT,
96 ssi_contexts::ODRL
97 );
98 pub static ref SCHEMA_ORG_CONTEXT_DOCUMENT: RemoteDocument = load_static_context(
99 SCHEMA_ORG_CONTEXT,
100 ssi_contexts::SCHEMA_ORG
101 );
102 pub static ref SECURITY_V1_CONTEXT_DOCUMENT: RemoteDocument = load_static_context(
103 SECURITY_V1_CONTEXT,
104 ssi_contexts::SECURITY_V1
105 );
106 pub static ref SECURITY_V2_CONTEXT_DOCUMENT: RemoteDocument = load_static_context(
107 SECURITY_V2_CONTEXT,
108 ssi_contexts::SECURITY_V2
109 );
110 pub static ref DID_V1_CONTEXT_DOCUMENT: RemoteDocument = load_static_context(
111 DID_V1_CONTEXT,
112 ssi_contexts::DID_V1
113 );
114 pub static ref DID_RESOLUTION_V1_CONTEXT_DOCUMENT: RemoteDocument = load_static_context(
115 DID_RESOLUTION_V1_CONTEXT,
116 ssi_contexts::DID_RESOLUTION_V1
117 );
118 pub static ref DIF_ESRS2020_CONTEXT_DOCUMENT: RemoteDocument = load_static_context(
120 #[allow(deprecated)]
121 DIF_ESRS2020_CONTEXT,
122 #[allow(deprecated)]
123 ssi_contexts::DIF_ESRS2020
124 );
125 pub static ref W3ID_ESRS2020_V2_CONTEXT_DOCUMENT: RemoteDocument = load_static_context(
126 W3ID_ESRS2020_V2_CONTEXT,
127 ssi_contexts::W3ID_ESRS2020_V2
128 );
129 pub static ref ESRS2020_EXTRA_CONTEXT_DOCUMENT: RemoteDocument = load_static_context(
131 #[allow(deprecated)]
132 ESRS2020_EXTRA_CONTEXT,
133 #[allow(deprecated)]
134 ssi_contexts::ESRS2020_EXTRA
135 );
136 pub static ref LDS_JWS2020_V1_CONTEXT_DOCUMENT: RemoteDocument = load_static_context(
137 LDS_JWS2020_V1_CONTEXT,
138 ssi_contexts::LDS_JWS2020_V1
139 );
140 pub static ref W3ID_JWS2020_V1_CONTEXT_DOCUMENT: RemoteDocument = load_static_context(
141 W3ID_JWS2020_V1_CONTEXT,
142 ssi_contexts::W3ID_JWS2020_V1
143 );
144 pub static ref W3ID_ED2020_V1_CONTEXT_DOCUMENT: RemoteDocument = load_static_context(
145 W3ID_ED2020_V1_CONTEXT,
146 ssi_contexts::W3ID_ED2020_V1
147 );
148 pub static ref W3ID_MULTIKEY_V1_CONTEXT_DOCUMENT: RemoteDocument = load_static_context(
149 W3ID_MULTIKEY_V1_CONTEXT,
150 ssi_contexts::W3ID_MULTIKEY_V1
151 );
152 pub static ref W3ID_DATA_INTEGRITY_V1_CONTEXT_DOCUMENT: RemoteDocument = load_static_context(
153 W3ID_DATA_INTEGRITY_V1_CONTEXT,
154 ssi_contexts::W3ID_DATA_INTEGRITY_V1
155 );
156 pub static ref W3ID_DATA_INTEGRITY_V2_CONTEXT_DOCUMENT: RemoteDocument = load_static_context(
157 W3ID_DATA_INTEGRITY_V2_CONTEXT,
158 ssi_contexts::W3ID_DATA_INTEGRITY_V2
159 );
160 pub static ref BLOCKCHAIN2021_V1_CONTEXT_DOCUMENT: RemoteDocument = load_static_context(
161 BLOCKCHAIN2021_V1_CONTEXT,
162 ssi_contexts::BLOCKCHAIN2021_V1
163 );
164 pub static ref CITIZENSHIP_V1_CONTEXT_DOCUMENT: RemoteDocument = load_static_context(
165 CITIZENSHIP_V1_CONTEXT,
166 ssi_contexts::CITIZENSHIP_V1
167 );
168 pub static ref VACCINATION_V1_CONTEXT_DOCUMENT: RemoteDocument = load_static_context(
169 VACCINATION_V1_CONTEXT,
170 ssi_contexts::VACCINATION_V1
171 );
172 pub static ref TRACEABILITY_CONTEXT_DOCUMENT: RemoteDocument = load_static_context(
173 TRACEABILITY_CONTEXT,
174 ssi_contexts::TRACEABILITY_V1
175 );
176 pub static ref REVOCATION_LIST_2020_V1_CONTEXT_DOCUMENT: RemoteDocument = load_static_context(
177 REVOCATION_LIST_2020_V1_CONTEXT,
178 ssi_contexts::REVOCATION_LIST_2020_V1
179 );
180 pub static ref STATUS_LIST_2021_V1_CONTEXT_DOCUMENT: RemoteDocument = load_static_context(
181 STATUS_LIST_2021_V1_CONTEXT,
182 ssi_contexts::STATUS_LIST_2021_V1
183 );
184 pub static ref EIP712SIG_V0_1_CONTEXT_DOCUMENT: RemoteDocument = load_static_context(
185 EIP712SIG_V0_1_CONTEXT,
186 ssi_contexts::EIP712SIG_V0_1
187 );
188 pub static ref BBS_V1_CONTEXT_DOCUMENT: RemoteDocument = load_static_context(
189 BBS_V1_CONTEXT,
190 ssi_contexts::BBS_V1
191 );
192 pub static ref EIP712SIG_V1_CONTEXT_DOCUMENT: RemoteDocument = load_static_context(
193 EIP712SIG_V1_CONTEXT,
194 ssi_contexts::EIP712SIG_V1
195 );
196 pub static ref PRESENTATION_SUBMISSION_V1_CONTEXT_DOCUMENT: RemoteDocument = load_static_context(
197 PRESENTATION_SUBMISSION_V1_CONTEXT,
198 ssi_contexts::PRESENTATION_SUBMISSION_V1
199 );
200 pub static ref VDL_V1_CONTEXT_DOCUMENT: RemoteDocument = load_static_context(
201 VDL_V1_CONTEXT,
202 ssi_contexts::VDL_V1
203 );
204 pub static ref WALLET_V1_CONTEXT_DOCUMENT: RemoteDocument = load_static_context(
205 WALLET_V1_CONTEXT,
206 ssi_contexts::WALLET_V1
207 );
208 pub static ref ZCAP_V1_CONTEXT_DOCUMENT: RemoteDocument = load_static_context(
209 ZCAP_V1_CONTEXT,
210 ssi_contexts::ZCAP_V1
211 );
212 pub static ref CACAO_ZCAP_V1_CONTEXT_DOCUMENT: RemoteDocument = load_static_context(
213 CACAO_ZCAP_V1_CONTEXT,
214 ssi_contexts::CACAO_ZCAP_V1
215 );
216 pub static ref JFF_VC_EDU_PLUGFEST_2022_CONTEXT_DOCUMENT: RemoteDocument = load_static_context(
217 JFF_VC_EDU_PLUGFEST_2022_CONTEXT,
218 ssi_contexts::JFF_VC_EDU_PLUGFEST_2022
219 );
220 pub static ref DID_CONFIGURATION_V0_0_CONTEXT_DOCUMENT: RemoteDocument = load_static_context(
221 DID_CONFIGURATION_V0_0_CONTEXT,
222 ssi_contexts::DID_CONFIGURATION_V0_0
223 );
224 pub static ref DID_CONFIGURATION_V1_CONTEXT_DOCUMENT: RemoteDocument = load_static_context(
225 DID_CONFIGURATION_V1_CONTEXT,
226 ssi_contexts::DID_CONFIGURATION_V1
227 );
228 pub static ref JFF_VC_EDU_PLUGFEST_2022_2_CONTEXT_DOCUMENT: RemoteDocument = load_static_context(
229 JFF_VC_EDU_PLUGFEST_2022_2_CONTEXT,
230 ssi_contexts::JFF_VC_EDU_PLUGFEST_2022_2
231 );
232 pub static ref LINKED_VP_V1_CONTEXT_DOCUMENT: RemoteDocument = load_static_context(
233 LINKED_VP_V1_CONTEXT,
234 ssi_contexts::LINKED_VP_V1
235 );
236}
237
238macro_rules! iri_match {
239 { match $input:ident { $($(#[$meta:meta])? $($id:ident)|* => $e:expr,)* _ as $default:ident => $de:expr } } => {
240 match $input {
241 $($(#[$meta])? $input if $($input == $id)||* => $e),*
242 $default => $de
243 }
244 };
245}
246
247#[derive(thiserror::Error, Debug)]
250#[error("Unknown context")]
251pub struct UnknownContext;
252
253#[derive(Clone)]
254pub struct StaticLoader;
255
256impl Loader for StaticLoader {
257 async fn load(&self, url: &Iri) -> json_ld::LoadingResult {
258 iri_match! {
259 match url {
260 CREDENTIALS_V1_CONTEXT => Ok(CREDENTIALS_V1_CONTEXT_DOCUMENT.clone()),
261 CREDENTIALS_V2_CONTEXT => Ok(CREDENTIALS_V2_CONTEXT_DOCUMENT.clone()),
262 CREDENTIALS_EXAMPLES_V1_CONTEXT => {
263 Ok(CREDENTIALS_EXAMPLES_V1_CONTEXT_DOCUMENT.clone())
264 },
265 CREDENTIALS_EXAMPLES_V2_CONTEXT => {
266 Ok(CREDENTIALS_EXAMPLES_V2_CONTEXT_DOCUMENT.clone())
267 },
268 ODRL_CONTEXT => Ok(ODRL_CONTEXT_DOCUMENT.clone()),
269 SECURITY_V1_CONTEXT => Ok(SECURITY_V1_CONTEXT_DOCUMENT.clone()),
270 SECURITY_V2_CONTEXT => Ok(SECURITY_V2_CONTEXT_DOCUMENT.clone()),
271 SCHEMA_ORG_CONTEXT => Ok(SCHEMA_ORG_CONTEXT_DOCUMENT.clone()),
272 DID_V1_CONTEXT | DID_V1_CONTEXT_NO_WWW | W3ID_DID_V1_CONTEXT => {
273 Ok(DID_V1_CONTEXT_DOCUMENT.clone())
274 },
275 DID_RESOLUTION_V1_CONTEXT => Ok(DID_RESOLUTION_V1_CONTEXT_DOCUMENT.clone()),
276 #[allow(deprecated)]
277 DIF_ESRS2020_CONTEXT => Ok(DIF_ESRS2020_CONTEXT_DOCUMENT.clone()),
278 W3ID_ESRS2020_V2_CONTEXT => Ok(W3ID_ESRS2020_V2_CONTEXT_DOCUMENT.clone()),
279 #[allow(deprecated)]
280 ESRS2020_EXTRA_CONTEXT => Ok(ESRS2020_EXTRA_CONTEXT_DOCUMENT.clone()),
281 LDS_JWS2020_V1_CONTEXT => Ok(LDS_JWS2020_V1_CONTEXT_DOCUMENT.clone()),
282 W3ID_JWS2020_V1_CONTEXT => Ok(W3ID_JWS2020_V1_CONTEXT_DOCUMENT.clone()),
283 W3ID_ED2020_V1_CONTEXT => Ok(W3ID_ED2020_V1_CONTEXT_DOCUMENT.clone()),
284 W3ID_MULTIKEY_V1_CONTEXT => Ok(W3ID_MULTIKEY_V1_CONTEXT_DOCUMENT.clone()),
285 W3ID_DATA_INTEGRITY_V1_CONTEXT => Ok(W3ID_DATA_INTEGRITY_V1_CONTEXT_DOCUMENT.clone()),
286 W3ID_DATA_INTEGRITY_V2_CONTEXT => Ok(W3ID_DATA_INTEGRITY_V2_CONTEXT_DOCUMENT.clone()),
287 BLOCKCHAIN2021_V1_CONTEXT => Ok(BLOCKCHAIN2021_V1_CONTEXT_DOCUMENT.clone()),
288 CITIZENSHIP_V1_CONTEXT => Ok(CITIZENSHIP_V1_CONTEXT_DOCUMENT.clone()),
289 VACCINATION_V1_CONTEXT => Ok(VACCINATION_V1_CONTEXT_DOCUMENT.clone()),
290 TRACEABILITY_CONTEXT => Ok(TRACEABILITY_CONTEXT_DOCUMENT.clone()),
291 REVOCATION_LIST_2020_V1_CONTEXT => {
292 Ok(REVOCATION_LIST_2020_V1_CONTEXT_DOCUMENT.clone())
293 },
294 STATUS_LIST_2021_V1_CONTEXT => Ok(STATUS_LIST_2021_V1_CONTEXT_DOCUMENT.clone()),
295 EIP712SIG_V0_1_CONTEXT => Ok(EIP712SIG_V0_1_CONTEXT_DOCUMENT.clone()),
296 BBS_V1_CONTEXT => Ok(BBS_V1_CONTEXT_DOCUMENT.clone()),
297 EIP712SIG_V1_CONTEXT => Ok(EIP712SIG_V1_CONTEXT_DOCUMENT.clone()),
298 PRESENTATION_SUBMISSION_V1_CONTEXT => {
299 Ok(PRESENTATION_SUBMISSION_V1_CONTEXT_DOCUMENT.clone())
300 },
301 VDL_V1_CONTEXT => Ok(VDL_V1_CONTEXT_DOCUMENT.clone()),
302 WALLET_V1_CONTEXT => Ok(WALLET_V1_CONTEXT_DOCUMENT.clone()),
303 ZCAP_V1_CONTEXT => Ok(ZCAP_V1_CONTEXT_DOCUMENT.clone()),
304 CACAO_ZCAP_V1_CONTEXT => Ok(CACAO_ZCAP_V1_CONTEXT_DOCUMENT.clone()),
305 JFF_VC_EDU_PLUGFEST_2022_CONTEXT => {
306 Ok(JFF_VC_EDU_PLUGFEST_2022_CONTEXT_DOCUMENT.clone())
307 },
308 DID_CONFIGURATION_V0_0_CONTEXT => {
309 Ok(DID_CONFIGURATION_V0_0_CONTEXT_DOCUMENT.clone())
310 },
311 DID_CONFIGURATION_V1_CONTEXT => {
312 Ok(DID_CONFIGURATION_V1_CONTEXT_DOCUMENT.clone())
313 },
314 JFF_VC_EDU_PLUGFEST_2022_2_CONTEXT => {
315 Ok(JFF_VC_EDU_PLUGFEST_2022_2_CONTEXT_DOCUMENT.clone())
316 },
317 LINKED_VP_V1_CONTEXT => { Ok(LINKED_VP_V1_CONTEXT_DOCUMENT.clone()) },
318 _ as iri => Err(LoadError::new(iri.to_owned(), UnknownContext))
319 }
320 }
321 }
322}
323
324pub type ContextMap = HashMap<IriBuf, RemoteDocument>;
325
326#[derive(Clone)]
327pub struct ContextLoader {
328 static_loader: Option<StaticLoader>,
330
331 context_map: Option<ContextMap>,
335}
336
337impl std::fmt::Debug for ContextLoader {
338 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
339 f.debug_struct("ContextLoader").finish_non_exhaustive()
340 }
341}
342
343#[derive(Debug, Error)]
349pub enum FromContextMapError {
350 #[error(transparent)]
351 ParseError(#[from] json_ld::syntax::parse::Error),
352
353 #[error(transparent)]
354 InvalidIri(iref::InvalidIri<String>),
355}
356
357impl From<iref::InvalidIri<String>> for FromContextMapError {
358 fn from(e: iref::InvalidIri<String>) -> Self {
359 Self::InvalidIri(e)
360 }
361}
362
363impl ContextLoader {
364 pub fn empty() -> Self {
366 Self {
367 static_loader: None,
368 context_map: None,
369 }
370 }
371 pub fn with_static_loader(mut self) -> Self {
374 self.static_loader = Some(StaticLoader);
375 self
376 }
377 pub fn with_context_map_from(
381 mut self,
382 preparsed_context_map: HashMap<String, String>,
383 ) -> Result<Self, FromContextMapError> {
384 let context_map = preparsed_context_map
385 .into_iter()
386 .map(
387 |(url, jsonld)| -> Result<(IriBuf, RemoteDocument), FromContextMapError> {
388 let (doc, _) = json_syntax::Value::parse_str(&jsonld)?;
389 let iri = IriBuf::new(url)?;
390 let remote_doc = RemoteDocument::new(
391 Some(iri.clone()),
392 Some("application/ld+json".parse().unwrap()),
393 doc,
394 );
395 Ok((iri, remote_doc))
396 },
397 )
398 .collect::<Result<HashMap<IriBuf, RemoteDocument>, FromContextMapError>>()?;
399 self.context_map = Some(context_map);
400 Ok(self)
401 }
402}
403
404impl std::default::Default for ContextLoader {
406 fn default() -> Self {
407 Self {
408 static_loader: Some(StaticLoader),
409 context_map: None,
410 }
411 }
412}
413
414impl Loader for ContextLoader {
415 async fn load(&self, url: &Iri) -> json_ld::LoadingResult {
416 let url = match &self.static_loader {
417 Some(static_loader) => {
418 match static_loader.load(url).await {
419 Ok(x) => {
420 return Ok(x);
422 }
423 Err(_) => {
424 url
428 }
429 }
430 }
431 None => url,
432 };
433
434 if let Some(context_map) = &self.context_map {
436 context_map
437 .get(url)
438 .cloned()
439 .ok_or_else(|| LoadError::new(url.to_owned(), UnknownContext))
440 } else {
441 Err(LoadError::new(url.to_owned(), UnknownContext))
442 }
443 }
444}
445
446#[derive(Debug, thiserror::Error)]
447pub enum ContextError {
448 #[error("Invalid JSON: {0}")]
449 InvalidJson(#[from] json_syntax::parse::Error),
450
451 #[error("Invalid JSON-LD context: {0}")]
452 InvalidContext(#[from] json_ld::syntax::context::InvalidContext),
453}
454
455pub fn parse_ld_context(content: &str) -> Result<RemoteContextReference, ContextError> {
457 let (json, _) = json_syntax::Value::parse_str(content)?;
458 let context = json_ld::syntax::Context::try_from_json(json)?;
459 Ok(RemoteContextReference::Loaded(RemoteContext::new(
460 None, None, context,
461 )))
462}
463
464#[cfg(test)]
465mod test {
466 use serde_json::json;
467
468 use super::*;
469
470 #[tokio::test]
471 async fn context_loader() {
472 let cl = ContextLoader::default().with_context_map_from([(
473 "https://w3id.org/age/v1".to_string(),
474 serde_json::to_string(&json!({
475 "@context": {
476 "@protected": "true",
477 "id": "@id",
478 "type": "@type",
479 "description": "https://schema.org/description",
480 "image": {
481 "@id": "https://schema.org/image",
482 "@type": "@id"
483 },
484 "name": "https://schema.org/name",
485 "overAge": {
486 "@id": "https://w3id.org/age#overAge",
487 "@type": "http://www.w3.org/2001/XMLSchema#positiveInteger"
488 },
489 "concealedIdToken": {
490 "@id": "https://w3id.org/cit#concealedIdToken",
491 "@type": "https://w3id.org/security#multibase"
492 },
493 "anchoredResource": {
494 "@type": "@id",
495 "@id": "https://w3id.org/security#anchoredResource"
496 },
497 "digestMultibase": {
498 "@id": "https://w3id.org/security#digestMultibase",
499 "@type": "https://w3id.org/security#multibase"
500 },
501 "PersonalPhotoCredential": "https://convenience.org/vocab#PersonalPhotoCredential",
502 "OverAgeTokenCredential": "https://w3id.org/age#OverAgeTokenCredential",
503 "VerifiableCredentialRefreshService2021": {
504 "@id": "https://w3id.org/vc-refresh-service#VerifiableCredentialRefreshService2021",
505 "@context": {
506 "@protected": true,
507 "url": {
508 "@id": "https://schema.org/url",
509 "@type": "@id"
510 },
511 "refreshToken": {
512 "@id": "https://w3id.org/vc-refresh-service#refreshToken",
513 "@type": "https://w3id.org/security#multibase"
514 }
515 }
516 },
517 "AgeVerificationCredential": "https://w3id.org/age#AgeVerificationCredential",
518 "AgeVerificationContainerCredential": "https://w3id.org/age#AgeVerificationContainerCredential"
519 }
520 })).unwrap())]
521 .iter()
522 .cloned()
523 .collect(),
524 ).unwrap() ;
525 cl.load_with(
526 &mut (),
527 IriBuf::new("https://w3id.org/age/v1".to_string()).unwrap(),
528 )
529 .await
530 .unwrap();
531 }
532}