did_utils/methods/
traits.rs

1use crate::methods::resolution::*;
2use async_trait::async_trait;
3
4use crate::{ldmodel::Context, methods::errors::DIDResolutionError};
5
6/// Abstract contract for DID methods.
7///
8/// Initially thought to encompass the signatures of different operations
9/// that a DID method is optionally expected to support, it eventually
10/// turned out DID methods might be too specific in their underlying modus
11/// operandus that such signatures would be counterproductive.
12///
13// TODO! Enrich this common interface.
14pub trait DIDMethod: DIDResolver {
15    /// Returns the DIDMethod's registered name, prefixed with `did:`,
16    /// e.g. did:key, did:web, etc.
17    fn name() -> String;
18
19    /// Extracts the supertrait resolver object.
20    fn resolver(&self) -> &dyn DIDResolver
21    where
22        Self: Sized,
23    {
24        self
25    }
26}
27
28/// Abstract contract for DID resolution.
29///
30/// [See DID Resolution Specification](https://w3c.github.io/did-resolution)
31#[async_trait]
32pub trait DIDResolver {
33    /// Resolves a DID address into its corresponding DID document.
34    async fn resolve(&self, did: &str, _options: &DIDResolutionOptions) -> ResolutionOutput;
35
36    /// Dereferences a DID URL into its corresponding resource.
37    async fn dereference(&self, did_url: &str, _options: &DereferencingOptions) -> DereferencingOutput {
38        let context = Context::SingleString(String::from("https://w3id.org/did-resolution/v1"));
39
40        let res = super::utils::parse_did_url(did_url);
41        if res.is_err() {
42            return DereferencingOutput {
43                context,
44                content: None,
45                dereferencing_metadata: Some(DIDResolutionMetadata {
46                    error: Some(DIDResolutionError::InvalidDidUrl),
47                    content_type: None,
48                    additional_properties: None,
49                }),
50                content_metadata: None,
51                additional_properties: None,
52            };
53        }
54
55        let (did, query, fragment) = res.unwrap();
56
57        let resolution_output = self.resolve(&did, _options).await;
58        if resolution_output.did_resolution_metadata.is_some() {
59            let error = resolution_output.did_resolution_metadata.as_ref().unwrap().error.clone();
60            if let Some(err) = error {
61                return DereferencingOutput {
62                    context,
63                    content: None,
64                    dereferencing_metadata: Some(DereferencingMetadata {
65                        error: if err == DIDResolutionError::InvalidDid {
66                            Some(DIDResolutionError::InvalidDidUrl)
67                        } else {
68                            Some(err)
69                        },
70                        content_type: None,
71                        additional_properties: None,
72                    }),
73                    content_metadata: None,
74                    additional_properties: None,
75                };
76            }
77        };
78
79        let diddoc = match &resolution_output.did_document {
80            Some(doc) => doc,
81            None => unreachable!("DID document must be present if no DIDResolutionError"),
82        };
83
84        match dereference_did_document(diddoc, &query, &fragment) {
85            Ok(content) => DereferencingOutput {
86                context,
87                content: Some(content.clone()),
88                dereferencing_metadata: Some(DereferencingMetadata {
89                    error: None,
90                    content_type: match content {
91                        Content::DIDDocument(_) => {
92                            if resolution_output.did_resolution_metadata.is_some() {
93                                resolution_output.did_resolution_metadata.unwrap().content_type
94                            } else {
95                                None
96                            }
97                        }
98                        _ => Some(MediaType::Json.to_string()),
99                    },
100                    additional_properties: None,
101                }),
102                content_metadata: None,
103                additional_properties: None,
104            },
105            Err(err) => DereferencingOutput {
106                context,
107                content: None,
108                dereferencing_metadata: Some(DereferencingMetadata {
109                    error: Some(err),
110                    content_type: None,
111                    additional_properties: None,
112                }),
113                content_metadata: None,
114                additional_properties: None,
115            },
116        }
117    }
118}