Skip to main content

typesec_integrations/did/
identifier.rs

1//! The [`Did`] decentralized-identifier type.
2
3use std::fmt;
4
5use serde::{Deserialize, Serialize};
6
7use super::crypto::hex_encode;
8use super::error::DidError;
9
10/// A decentralized identifier string.
11#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
12#[serde(try_from = "String", into = "String")]
13pub struct Did(String);
14
15impl Did {
16    /// Parse a DID.
17    pub fn parse(value: impl Into<String>) -> Result<Self, DidError> {
18        let value = value.into();
19        let parts: Vec<_> = value.split(':').collect();
20        if parts.len() < 3 || parts.first() != Some(&"did") || parts[1].is_empty() {
21            return Err(DidError::InvalidDid(value));
22        }
23        Ok(Self(value))
24    }
25
26    /// Create a deterministic `did:key` identifier from public key material.
27    pub fn key(public_key: impl AsRef<[u8]>) -> Self {
28        Self(format!("did:key:z{}", hex_encode(public_key.as_ref())))
29    }
30
31    /// Create a `did:web` identifier for a host.
32    pub fn web(host: impl AsRef<str>) -> Result<Self, DidError> {
33        let host = host.as_ref().trim();
34        if host.is_empty() || host.contains('/') {
35            return Err(DidError::InvalidDid(format!("did:web:{host}")));
36        }
37        Ok(Self(format!("did:web:{host}")))
38    }
39
40    /// Borrow the DID as a string.
41    pub fn as_str(&self) -> &str {
42        &self.0
43    }
44}
45
46impl fmt::Display for Did {
47    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48        f.write_str(&self.0)
49    }
50}
51
52impl TryFrom<String> for Did {
53    type Error = DidError;
54
55    fn try_from(value: String) -> Result<Self, Self::Error> {
56        Self::parse(value)
57    }
58}
59
60impl From<Did> for String {
61    fn from(value: Did) -> Self {
62        value.0
63    }
64}