1use core::fmt;
5use core::hash::{Hash, Hasher};
6use core::str::FromStr;
7use std::error::Error;
8use std::sync::Arc;
9
10use indexmap::IndexMap;
11use serde::de::Error as _;
12use serde::{Deserialize, Deserializer, Serialize, Serializer};
13use serde_json::Value;
14
15use super::{Did, DidDocument, DidError};
16
17pub const APPLICATION_DID_JSON: &str = "application/did+json";
19pub const APPLICATION_DID_LD_JSON: &str = "application/did+ld+json";
21
22#[derive(Debug, Clone, PartialEq, Eq, Hash)]
27#[non_exhaustive]
28pub enum ResolutionErrorKind {
29 InvalidDid,
31 NotFound,
33 MethodNotSupported,
35 RepresentationNotSupported,
37 InvalidDidDocument,
40 InternalError,
42 Other(String),
44}
45
46impl ResolutionErrorKind {
47 pub fn as_str(&self) -> &str {
49 match self {
50 Self::InvalidDid => "invalidDid",
51 Self::NotFound => "notFound",
52 Self::MethodNotSupported => "methodNotSupported",
53 Self::RepresentationNotSupported => "representationNotSupported",
54 Self::InvalidDidDocument => "invalidDidDocument",
55 Self::InternalError => "internalError",
56 Self::Other(keyword) => keyword,
57 }
58 }
59}
60
61impl fmt::Display for ResolutionErrorKind {
62 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64 match self {
65 Self::InvalidDid => f.write_str("invalid DID"),
66 Self::NotFound => f.write_str("DID not found"),
67 Self::MethodNotSupported => f.write_str("DID method not supported"),
68 Self::RepresentationNotSupported => f.write_str("representation not supported"),
69 Self::InvalidDidDocument => f.write_str("invalid DID document"),
70 Self::InternalError => f.write_str("internal resolver error"),
71 Self::Other(keyword) => write!(f, "resolution error `{keyword}`"),
72 }
73 }
74}
75
76impl FromStr for ResolutionErrorKind {
77 type Err = core::convert::Infallible;
78
79 fn from_str(s: &str) -> Result<Self, Self::Err> {
81 Ok(match s {
82 "invalidDid" => Self::InvalidDid,
83 "notFound" => Self::NotFound,
84 "methodNotSupported" => Self::MethodNotSupported,
85 "representationNotSupported" => Self::RepresentationNotSupported,
86 "invalidDidDocument" => Self::InvalidDidDocument,
87 "internalError" => Self::InternalError,
88 other => Self::Other(other.to_owned()),
89 })
90 }
91}
92
93#[derive(Debug, Clone)]
128pub struct ResolutionError {
129 kind: ResolutionErrorKind,
130 detail: Option<String>,
131 source: Option<Arc<dyn Error + Send + Sync + 'static>>,
132}
133
134impl ResolutionError {
135 pub const fn new(kind: ResolutionErrorKind) -> Self {
137 Self {
138 kind,
139 detail: None,
140 source: None,
141 }
142 }
143
144 #[must_use]
146 pub fn with_detail(mut self, detail: impl Into<String>) -> Self {
147 self.detail = Some(detail.into());
148 self
149 }
150
151 #[must_use]
153 pub fn with_source<E: Error + Send + Sync + 'static>(mut self, source: E) -> Self {
154 self.source = Some(Arc::new(source));
155 self
156 }
157
158 pub const fn kind(&self) -> &ResolutionErrorKind {
160 &self.kind
161 }
162
163 pub fn as_str(&self) -> &str {
165 self.kind.as_str()
166 }
167
168 pub fn detail(&self) -> Option<&str> {
170 self.detail.as_deref()
171 }
172}
173
174impl fmt::Display for ResolutionError {
175 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178 fmt::Display::fmt(&self.kind, f)?;
179 if let Some(detail) = &self.detail {
180 write!(f, ": {detail}")?;
181 }
182 if let Some(source) = &self.source {
183 write!(f, ": {source}")?;
184 }
185 Ok(())
186 }
187}
188
189impl Error for ResolutionError {
190 fn source(&self) -> Option<&(dyn Error + 'static)> {
191 self.source
192 .as_deref()
193 .map(|source| source as &(dyn Error + 'static))
194 }
195}
196
197impl PartialEq for ResolutionError {
200 fn eq(&self, other: &Self) -> bool {
201 self.kind == other.kind
202 }
203}
204
205impl Eq for ResolutionError {}
206
207impl PartialEq<ResolutionErrorKind> for ResolutionError {
208 fn eq(&self, other: &ResolutionErrorKind) -> bool {
209 self.kind == *other
210 }
211}
212
213impl Hash for ResolutionError {
214 fn hash<H: Hasher>(&self, state: &mut H) {
215 self.kind.hash(state);
216 }
217}
218
219impl From<ResolutionErrorKind> for ResolutionError {
220 fn from(kind: ResolutionErrorKind) -> Self {
221 Self::new(kind)
222 }
223}
224
225impl FromStr for ResolutionError {
226 type Err = core::convert::Infallible;
227
228 fn from_str(s: &str) -> Result<Self, Self::Err> {
230 s.parse().map(Self::new)
231 }
232}
233
234impl From<DidError> for ResolutionError {
235 fn from(error: DidError) -> Self {
237 Self::new(ResolutionErrorKind::InvalidDid).with_source(error)
238 }
239}
240
241impl Serialize for ResolutionError {
242 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
243 serializer.serialize_str(self.as_str())
244 }
245}
246
247impl<'de> Deserialize<'de> for ResolutionError {
248 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
249 let keyword = String::deserialize(deserializer)?;
250 if keyword.is_empty() || !keyword.is_ascii() {
251 return Err(D::Error::custom("error must be an ASCII keyword"));
252 }
253 keyword.parse().map_err(|never| match never {})
254 }
255}
256
257#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
259#[non_exhaustive]
260pub struct ResolutionOptions {
261 #[serde(default, skip_serializing_if = "Option::is_none")]
263 pub accept: Option<String>,
264 #[serde(flatten)]
266 pub properties: IndexMap<String, Value>,
267}
268
269impl ResolutionOptions {
270 pub fn accept(media_type: impl Into<String>) -> Self {
272 Self {
273 accept: Some(media_type.into()),
274 properties: IndexMap::new(),
275 }
276 }
277}
278
279#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
281#[non_exhaustive]
282pub struct ResolutionMetadata {
283 #[serde(
285 rename = "contentType",
286 default,
287 skip_serializing_if = "Option::is_none"
288 )]
289 pub content_type: Option<String>,
290 #[serde(default, skip_serializing_if = "Option::is_none")]
293 pub error: Option<ResolutionError>,
294 #[serde(flatten)]
296 pub properties: IndexMap<String, Value>,
297}
298
299impl ResolutionMetadata {
300 pub fn error(error: impl Into<ResolutionError>) -> Self {
302 Self {
303 content_type: None,
304 error: Some(error.into()),
305 properties: IndexMap::new(),
306 }
307 }
308
309 pub fn content_type(media_type: impl Into<String>) -> Self {
311 Self {
312 content_type: Some(media_type.into()),
313 error: None,
314 properties: IndexMap::new(),
315 }
316 }
317}
318
319#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
324#[non_exhaustive]
325pub struct DocumentMetadata {
326 #[serde(default, skip_serializing_if = "Option::is_none")]
328 pub created: Option<String>,
329 #[serde(default, skip_serializing_if = "Option::is_none")]
331 pub updated: Option<String>,
332 #[serde(default, skip_serializing_if = "Option::is_none")]
334 pub deactivated: Option<bool>,
335 #[serde(
337 rename = "nextUpdate",
338 default,
339 skip_serializing_if = "Option::is_none"
340 )]
341 pub next_update: Option<String>,
342 #[serde(rename = "versionId", default, skip_serializing_if = "Option::is_none")]
344 pub version_id: Option<String>,
345 #[serde(
347 rename = "nextVersionId",
348 default,
349 skip_serializing_if = "Option::is_none"
350 )]
351 pub next_version_id: Option<String>,
352 #[serde(
354 rename = "equivalentId",
355 default,
356 skip_serializing_if = "Vec::is_empty"
357 )]
358 pub equivalent_id: Vec<Did>,
359 #[serde(
361 rename = "canonicalId",
362 default,
363 skip_serializing_if = "Option::is_none"
364 )]
365 pub canonical_id: Option<Did>,
366 #[serde(flatten)]
368 pub properties: IndexMap<String, Value>,
369}
370
371impl DocumentMetadata {
372 pub fn is_deactivated(&self) -> bool {
374 self.deactivated == Some(true)
375 }
376}
377
378#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
395#[non_exhaustive]
396pub struct ResolutionResult {
397 #[serde(
399 rename = "didDocument",
400 default,
401 skip_serializing_if = "Option::is_none"
402 )]
403 pub document: Option<DidDocument>,
404 #[serde(rename = "didResolutionMetadata", default)]
406 pub resolution_metadata: ResolutionMetadata,
407 #[serde(rename = "didDocumentMetadata", default)]
409 pub document_metadata: DocumentMetadata,
410}
411
412impl ResolutionResult {
413 pub fn new(document: DidDocument) -> Self {
415 Self {
416 document: Some(document),
417 resolution_metadata: ResolutionMetadata::default(),
418 document_metadata: DocumentMetadata::default(),
419 }
420 }
421
422 pub fn error(error: impl Into<ResolutionError>) -> Self {
425 Self {
426 document: None,
427 resolution_metadata: ResolutionMetadata::error(error),
428 document_metadata: DocumentMetadata::default(),
429 }
430 }
431
432 #[must_use]
434 pub fn with_document_metadata(mut self, metadata: DocumentMetadata) -> Self {
435 self.document_metadata = metadata;
436 self
437 }
438
439 #[must_use]
441 pub fn with_resolution_metadata(mut self, metadata: ResolutionMetadata) -> Self {
442 self.resolution_metadata = metadata;
443 self
444 }
445
446 pub fn error_kind(&self) -> Option<&ResolutionErrorKind> {
449 self.resolution_metadata
450 .error
451 .as_ref()
452 .map(ResolutionError::kind)
453 }
454
455 pub fn into_document(self) -> Result<DidDocument, ResolutionError> {
462 match (self.document, self.resolution_metadata.error) {
463 (_, Some(error)) => Err(error),
464 (Some(document), None) => Ok(document),
465 (None, None) => Err(ResolutionErrorKind::NotFound.into()),
466 }
467 }
468}
469
470impl fmt::Display for ResolutionResult {
471 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
474 match serde_json::to_string(self) {
475 Ok(json) => f.write_str(&json),
476 Err(e) => write!(f, "<unserializable resolution result: {e}>"),
477 }
478 }
479}