1use alloc::{
19 borrow::ToOwned,
20 collections::BTreeMap,
21 string::{String, ToString},
22 sync::Arc,
23 vec::Vec,
24};
25use serde_json::Value;
26
27struct Bundled {
29 iri: &'static str,
30 aliases: &'static [&'static str],
34 document: &'static str,
35}
36
37const BUNDLED: &[Bundled] = &[
38 Bundled {
39 iri: "https://www.w3.org/ns/activitystreams",
40 aliases: &[
41 "http://www.w3.org/ns/activitystreams",
42 "https://www.w3.org/ns/activitystreams#",
43 ],
44 document: include_str!("../contexts/activitystreams.jsonld"),
45 },
46 Bundled {
47 iri: "https://w3id.org/security/v1",
48 aliases: &["http://w3id.org/security/v1"],
49 document: include_str!("../contexts/security-v1.jsonld"),
50 },
51 Bundled {
54 iri: "https://w3id.org/identity/v1",
55 aliases: &["http://w3id.org/identity/v1"],
56 document: include_str!("../contexts/identity-v1.jsonld"),
57 },
58 Bundled {
59 iri: "https://w3id.org/security/data-integrity/v1",
60 aliases: &[],
61 document: include_str!("../contexts/security-data-integrity-v1.jsonld"),
62 },
63 Bundled {
64 iri: "https://w3id.org/security/data-integrity/v2",
65 aliases: &["https://www.w3.org/2025/credentials/vcdi/context/v2.jsonld"],
67 document: include_str!("../contexts/security-data-integrity-v2.jsonld"),
68 },
69 Bundled {
70 iri: "https://w3id.org/security/multikey/v1",
71 aliases: &[],
72 document: include_str!("../contexts/security-multikey-v1.jsonld"),
73 },
74 Bundled {
75 iri: "https://www.w3.org/ns/did/v1",
76 aliases: &[],
77 document: include_str!("../contexts/did-v1.jsonld"),
78 },
79 Bundled {
80 iri: "https://www.w3.org/ns/cid/v1",
81 aliases: &[],
82 document: include_str!("../contexts/cid-v1.jsonld"),
83 },
84 Bundled {
85 iri: "https://gotosocial.org/ns",
86 aliases: &["https://gotosocial.org/ns#"],
87 document: include_str!("../contexts/gotosocial.jsonld"),
88 },
89 Bundled {
90 iri: "https://litepub.social/litepub/context.jsonld",
91 aliases: &["http://litepub.social/litepub/context.jsonld"],
92 document: include_str!("../contexts/litepub.jsonld"),
93 },
94 Bundled {
95 iri: "https://purl.archive.org/socialweb/webfinger",
96 aliases: &[],
97 document: include_str!("../contexts/webfinger.jsonld"),
98 },
99 Bundled {
100 iri: "https://w3id.org/fep/ef61",
101 aliases: &[],
102 document: include_str!("../contexts/fep-ef61.jsonld"),
103 },
104 Bundled {
105 iri: "https://w3id.org/fep/7aa9",
106 aliases: &[],
107 document: include_str!("../contexts/fep-7aa9.jsonld"),
108 },
109 Bundled {
110 iri: "https://w3id.org/fep/22cd",
111 aliases: &[],
112 document: include_str!("../contexts/fep-22cd.jsonld"),
113 },
114 Bundled {
117 iri: "https://join-lemmy.org/context.json",
118 aliases: &[],
119 document: include_str!("../contexts/join-lemmy.jsonld"),
120 },
121 Bundled {
124 iri: "http://joinmastodon.org/ns",
125 aliases: &["https://joinmastodon.org/ns", "http://joinmastodon.org/ns#"],
126 document: include_str!("../contexts/joinmastodon.jsonld"),
127 },
128];
129
130#[derive(Clone, Debug)]
137pub struct Registry {
138 base: Arc<BTreeMap<String, Value>>,
140 added: BTreeMap<String, Value>,
142}
143
144impl Default for Registry {
145 fn default() -> Self {
146 Self::bundled()
147 }
148}
149
150impl Registry {
151 #[must_use]
157 pub fn bundled() -> Self {
158 let mut documents = BTreeMap::new();
159 for entry in BUNDLED {
160 let parsed: Value = serde_json::from_str(entry.document)
161 .expect("bundled context document is valid JSON");
162 for iri in core::iter::once(entry.iri).chain(entry.aliases.iter().copied()) {
163 documents.insert(iri.to_owned(), parsed.clone());
164 }
165 }
166 Self {
167 base: Arc::new(documents),
168 added: BTreeMap::new(),
169 }
170 }
171
172 #[must_use]
178 pub fn empty() -> Self {
179 Self {
180 base: Arc::new(BTreeMap::new()),
181 added: BTreeMap::new(),
182 }
183 }
184
185 #[must_use]
190 pub fn with(mut self, iri: impl Into<String>, document: Value) -> Self {
191 self.added.insert(iri.into(), document);
192 self
193 }
194
195 #[must_use]
197 pub fn knows(&self, iri: &str) -> bool {
198 self.added.contains_key(iri) || self.base.contains_key(iri)
199 }
200
201 pub(crate) fn resolve(&self, iri: &str) -> Option<&Value> {
203 self.added
204 .get(iri)
205 .or_else(|| self.base.get(iri))
206 .and_then(|doc| doc.get("@context"))
207 }
208
209 #[must_use]
211 pub fn known_iris(&self) -> Vec<String> {
212 let mut iris: Vec<String> = self
213 .base
214 .keys()
215 .filter(|iri| !self.added.contains_key(*iri))
216 .chain(self.added.keys())
217 .map(ToString::to_string)
218 .collect();
219 iris.sort();
220 iris
221 }
222}