Skip to main content

did_ion/sidetree/
did.rs

1use core::fmt;
2use std::{marker::PhantomData, str::FromStr};
3
4use serde::{Deserialize, Serialize};
5use ssi_dids_core::registration::DIDTransactionCreationError;
6
7use super::{InvalidSidetreeDIDSuffix, Sidetree};
8
9/// A Sidetree-based DID
10///
11/// Reference: [Sidetree §9. DID URI Composition][duc]
12///
13/// [duc]: https://identity.foundation/sidetree/spec/v1.0.0/#did-uri-composition
14pub enum SidetreeDID<S: Sidetree> {
15    /// Short-form Sidetree DID
16    ///
17    /// Reference: [§9. DID URI Composition](https://identity.foundation/sidetree/spec/v1.0.0/#short-form-did)
18    Short { did_suffix: DIDSuffix },
19
20    /// Long-form Sidetree DID
21    ///
22    /// Reference: [§9.1 Long-Form DID URIs](https://identity.foundation/sidetree/spec/v1.0.0/#long-form-did-uris)
23    Long {
24        did_suffix: DIDSuffix,
25        create_operation_data: String,
26        _marker: PhantomData<S>,
27    },
28}
29
30#[derive(Debug, thiserror::Error)]
31pub enum InvalidSidetreeDID {
32    #[error("invalid URI scheme")]
33    InvalidURIScheme,
34
35    #[error("DID method mismatch")]
36    DIDMethodMismatch,
37
38    #[error("Sidetree network mismatch")]
39    SidetreeNetworkMismatch,
40
41    #[error("missing sidetree DID suffix")]
42    MissingSidetreeDIDSuffix,
43
44    #[error(transparent)]
45    InvalidSidetreeDIDSuffix(#[from] InvalidSidetreeDIDSuffix),
46
47    #[error("unexpected data after Sidetree Long-Form DID")]
48    UnexpectedData,
49}
50
51impl<S: Sidetree> FromStr for SidetreeDID<S> {
52    type Err = InvalidSidetreeDID;
53
54    fn from_str(did: &str) -> Result<Self, Self::Err> {
55        let mut parts = did.split(':');
56
57        if parts.next() != Some("did") {
58            return Err(InvalidSidetreeDID::InvalidURIScheme);
59        }
60
61        if parts.next() != Some(S::METHOD) {
62            return Err(InvalidSidetreeDID::DIDMethodMismatch);
63        }
64
65        if let Some(network) = S::NETWORK {
66            if parts.next() != Some(network) {
67                return Err(InvalidSidetreeDID::SidetreeNetworkMismatch);
68            }
69        }
70
71        let did_suffix_str = parts
72            .next()
73            .ok_or(InvalidSidetreeDID::MissingSidetreeDIDSuffix)?;
74        let did_suffix = DIDSuffix(did_suffix_str.to_string());
75        S::validate_did_suffix(&did_suffix)?;
76        let create_operation_data_opt = parts.next();
77        if parts.next().is_some() {
78            return Err(InvalidSidetreeDID::UnexpectedData);
79        }
80        Ok(match create_operation_data_opt {
81            None => Self::Short { did_suffix },
82            Some(data) => Self::Long {
83                did_suffix,
84                create_operation_data: data.to_string(),
85                _marker: PhantomData,
86            },
87        })
88    }
89}
90
91impl<S: Sidetree> fmt::Display for SidetreeDID<S> {
92    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
93        write!(f, "did:{}:", S::METHOD)?;
94        if let Some(network) = S::NETWORK {
95            write!(f, "{}:", network)?;
96        }
97        match self {
98            Self::Short { did_suffix } => f.write_str(&did_suffix.0),
99            Self::Long {
100                did_suffix,
101                create_operation_data,
102                _marker,
103            } => write!(f, "{}:{}", did_suffix.0, create_operation_data),
104        }
105    }
106}
107
108impl From<InvalidSidetreeDID> for DIDTransactionCreationError {
109    fn from(_value: InvalidSidetreeDID) -> Self {
110        DIDTransactionCreationError::InvalidDID
111    }
112}
113
114/// [DID Suffix](https://identity.foundation/sidetree/spec/v1.0.0/#did-suffix)
115///
116/// Unique identifier string within a Sidetree DID (short or long-form)
117#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
118pub struct DIDSuffix(pub String);
119
120impl fmt::Display for DIDSuffix {
121    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
122        write!(f, "{}", self.0)?;
123        Ok(())
124    }
125}
126
127impl<S: Sidetree> From<SidetreeDID<S>> for DIDSuffix {
128    fn from(did: SidetreeDID<S>) -> DIDSuffix {
129        match did {
130            SidetreeDID::Short { did_suffix } => did_suffix,
131            SidetreeDID::Long { did_suffix, .. } => did_suffix,
132        }
133    }
134}