json_ld_context_processing/
lib.rs1use algorithm::{Action, RejectVocab};
3pub use json_ld_core::{warning, Context, ProcessingMode};
4use json_ld_core::{ExtractContextError, LoadError, Loader};
5use json_ld_syntax::ErrorCode;
6use rdf_types::VocabularyMut;
7use std::{fmt, hash::Hash};
8
9pub mod algorithm;
10mod processed;
11mod stack;
12
13pub use processed::*;
14pub use stack::ProcessingStack;
15
16pub enum Warning {
18 KeywordLikeTerm(String),
19 KeywordLikeValue(String),
20 MalformedIri(String),
21}
22
23impl fmt::Display for Warning {
24 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
25 match self {
26 Self::KeywordLikeTerm(s) => write!(f, "keyword-like term `{s}`"),
27 Self::KeywordLikeValue(s) => write!(f, "keyword-like value `{s}`"),
28 Self::MalformedIri(s) => write!(f, "malformed IRI `{s}`"),
29 }
30 }
31}
32
33impl<N> contextual::DisplayWithContext<N> for Warning {
34 fn fmt_with(&self, _: &N, f: &mut fmt::Formatter) -> fmt::Result {
35 fmt::Display::fmt(self, f)
36 }
37}
38
39pub trait WarningHandler<N>: json_ld_core::warning::Handler<N, Warning> {}
40
41impl<N, H> WarningHandler<N> for H where H: json_ld_core::warning::Handler<N, Warning> {}
42
43#[derive(Debug, thiserror::Error)]
45pub enum Error {
46 #[error("Invalid context nullification")]
47 InvalidContextNullification,
48
49 #[error("Remote document loading failed")]
50 LoadingDocumentFailed,
51
52 #[error("Processing mode conflict")]
53 ProcessingModeConflict,
54
55 #[error("Invalid `@context` entry")]
56 InvalidContextEntry,
57
58 #[error("Invalid `@import` value")]
59 InvalidImportValue,
60
61 #[error("Invalid remote context")]
62 InvalidRemoteContext,
63
64 #[error("Invalid base IRI")]
65 InvalidBaseIri,
66
67 #[error("Invalid vocabulary mapping")]
68 InvalidVocabMapping,
69
70 #[error("Cyclic IRI mapping")]
71 CyclicIriMapping,
72
73 #[error("Invalid term definition")]
74 InvalidTermDefinition,
75
76 #[error("Keyword redefinition")]
77 KeywordRedefinition,
78
79 #[error("Invalid `@protected` value")]
80 InvalidProtectedValue,
81
82 #[error("Invalid type mapping")]
83 InvalidTypeMapping,
84
85 #[error("Invalid reverse property")]
86 InvalidReverseProperty,
87
88 #[error("Invalid IRI mapping")]
89 InvalidIriMapping,
90
91 #[error("Invalid keyword alias")]
92 InvalidKeywordAlias,
93
94 #[error("Invalid container mapping")]
95 InvalidContainerMapping,
96
97 #[error("Invalid scoped context")]
98 InvalidScopedContext,
99
100 #[error("Protected term redefinition")]
101 ProtectedTermRedefinition,
102
103 #[error(transparent)]
104 ContextLoadingFailed(#[from] LoadError),
105
106 #[error("Unable to extract JSON-LD context: {0}")]
107 ContextExtractionFailed(ExtractContextError),
108
109 #[error("Use of forbidden `@vocab`")]
110 ForbiddenVocab,
111}
112
113impl From<RejectVocab> for Error {
114 fn from(_value: RejectVocab) -> Self {
115 Self::ForbiddenVocab
116 }
117}
118
119impl Error {
120 pub fn code(&self) -> ErrorCode {
121 match self {
122 Self::InvalidContextNullification => ErrorCode::InvalidContextNullification,
123 Self::LoadingDocumentFailed => ErrorCode::LoadingDocumentFailed,
124 Self::ProcessingModeConflict => ErrorCode::ProcessingModeConflict,
125 Self::InvalidContextEntry => ErrorCode::InvalidContextEntry,
126 Self::InvalidImportValue => ErrorCode::InvalidImportValue,
127 Self::InvalidRemoteContext => ErrorCode::InvalidRemoteContext,
128 Self::InvalidBaseIri => ErrorCode::InvalidBaseIri,
129 Self::InvalidVocabMapping => ErrorCode::InvalidVocabMapping,
130 Self::CyclicIriMapping => ErrorCode::CyclicIriMapping,
131 Self::InvalidTermDefinition => ErrorCode::InvalidTermDefinition,
132 Self::KeywordRedefinition => ErrorCode::KeywordRedefinition,
133 Self::InvalidProtectedValue => ErrorCode::InvalidPropagateValue,
134 Self::InvalidTypeMapping => ErrorCode::InvalidTypeMapping,
135 Self::InvalidReverseProperty => ErrorCode::InvalidReverseProperty,
136 Self::InvalidIriMapping => ErrorCode::InvalidIriMapping,
137 Self::InvalidKeywordAlias => ErrorCode::InvalidKeywordAlias,
138 Self::InvalidContainerMapping => ErrorCode::InvalidContainerMapping,
139 Self::InvalidScopedContext => ErrorCode::InvalidScopedContext,
140 Self::ProtectedTermRedefinition => ErrorCode::ProtectedTermRedefinition,
141 Self::ContextLoadingFailed(_) => ErrorCode::LoadingRemoteContextFailed,
142 Self::ContextExtractionFailed(_) => ErrorCode::LoadingRemoteContextFailed,
143 Self::ForbiddenVocab => ErrorCode::InvalidVocabMapping,
144 }
145 }
146}
147
148pub type ProcessingResult<'a, T, B> = Result<Processed<'a, T, B>, Error>;
150
151pub trait Process {
152 #[allow(async_fn_in_trait)]
154 async fn process_full<N, L, W>(
155 &self,
156 vocabulary: &mut N,
157 active_context: &Context<N::Iri, N::BlankId>,
158 loader: &L,
159 base_url: Option<N::Iri>,
160 options: Options,
161 warnings: W,
162 ) -> Result<Processed<N::Iri, N::BlankId>, Error>
163 where
164 N: VocabularyMut,
165 N::Iri: Clone + Eq + Hash,
166 N::BlankId: Clone + PartialEq,
167 L: Loader,
168 W: WarningHandler<N>;
169
170 #[allow(clippy::type_complexity)]
172 #[allow(async_fn_in_trait)]
173 async fn process_with<N, L>(
174 &self,
175 vocabulary: &mut N,
176 active_context: &Context<N::Iri, N::BlankId>,
177 loader: &L,
178 base_url: Option<N::Iri>,
179 options: Options,
180 ) -> Result<Processed<N::Iri, N::BlankId>, Error>
181 where
182 N: VocabularyMut,
183 N::Iri: Clone + Eq + Hash,
184 N::BlankId: Clone + PartialEq,
185 L: Loader,
186 {
187 self.process_full(
188 vocabulary,
189 active_context,
190 loader,
191 base_url,
192 options,
193 warning::Print,
194 )
195 .await
196 }
197
198 #[allow(async_fn_in_trait)]
201 async fn process<N, L>(
202 &self,
203 vocabulary: &mut N,
204 loader: &L,
205 base_url: Option<N::Iri>,
206 ) -> Result<Processed<N::Iri, N::BlankId>, Error>
207 where
208 N: VocabularyMut,
209 N::Iri: Clone + Eq + Hash,
210 N::BlankId: Clone + PartialEq,
211 L: Loader,
212 {
213 let active_context = Context::default();
214 self.process_full(
215 vocabulary,
216 &active_context,
217 loader,
218 base_url,
219 Options::default(),
220 warning::Print,
221 )
222 .await
223 }
224}
225
226#[derive(Clone, Copy, PartialEq, Eq)]
228pub struct Options {
229 pub processing_mode: ProcessingMode,
231
232 pub override_protected: bool,
234
235 pub propagate: bool,
237
238 pub vocab: Action,
240}
241
242impl Options {
243 #[must_use]
245 pub fn with_override(&self) -> Options {
246 let mut opt = *self;
247 opt.override_protected = true;
248 opt
249 }
250
251 #[must_use]
253 pub fn with_no_override(&self) -> Options {
254 let mut opt = *self;
255 opt.override_protected = false;
256 opt
257 }
258
259 #[must_use]
261 pub fn without_propagation(&self) -> Options {
262 let mut opt = *self;
263 opt.propagate = false;
264 opt
265 }
266}
267
268impl Default for Options {
269 fn default() -> Options {
270 Options {
271 processing_mode: ProcessingMode::default(),
272 override_protected: false,
273 propagate: true,
274 vocab: Action::Keep,
275 }
276 }
277}