Skip to main content

affinidi_did_common/did_method/
mod.rs

1use std::fmt;
2
3use affinidi_encoding::Codec;
4use serde::{Deserialize, Serialize};
5
6pub(crate) mod identifier;
7pub mod key;
8pub(crate) mod parse;
9pub(crate) mod peer;
10pub(crate) mod resolve;
11
12use crate::did_method::peer::PeerNumAlgo;
13
14/// DID method identifiers per W3C DID Core 1.0
15#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
16#[non_exhaustive]
17pub enum DIDMethod {
18    Key {
19        /// Raw identifier for Display ("z6MkhaX...")
20        identifier: String,
21        /// Multicodec indicating key type
22        codec: Codec,
23        /// Raw public key bytes
24        key_bytes: Vec<u8>,
25    },
26    Peer {
27        /// Raw identifier for Display
28        identifier: String,
29        /// Algorithm number (0, 1, 2)
30        numalgo: PeerNumAlgo,
31    },
32    Web {
33        /// Raw identifier for Display ("example.com:user:alice")
34        identifier: String,
35        /// Domain (first segment)
36        domain: String,
37        /// Path segments after domain
38        path_segments: Vec<String>,
39    },
40    Jwk {
41        /// Raw identifier (base64url encoded JWK)
42        identifier: String,
43        // Could add parsed JWK later if needed
44    },
45    /// Ethereum DID method (did:ethr)
46    Ethr {
47        /// Raw identifier for Display (hex address)
48        identifier: String,
49    },
50    /// Public Key Hash DID method (did:pkh)
51    /// Format: <chain_namespace>:<chain_reference>:<account_address>
52    Pkh {
53        /// Raw identifier for Display
54        identifier: String,
55        /// Chain namespace (e.g., "eip155", "solana", "bip122")
56        chain_namespace: String,
57        /// Chain reference (network identifier)
58        chain_reference: String,
59        /// Account address
60        account_address: String,
61    },
62    /// WebVH DID method with versioned history (did:webvh)
63    /// Format: <scid>:<domain>:<path_segments>
64    Webvh {
65        /// Raw identifier for Display
66        identifier: String,
67        /// Self-Certifying IDentifier (hash)
68        scid: String,
69        /// Domain (first segment after SCID)
70        domain: String,
71        /// Path segments after domain
72        path_segments: Vec<String>,
73    },
74    /// Cheqd DID method (did:cheqd)
75    /// Format: <network>:<uuid>
76    Cheqd {
77        /// Raw identifier for Display
78        identifier: String,
79        /// Network (mainnet, testnet)
80        network: String,
81        /// UUID identifier
82        uuid: String,
83    },
84    /// SCID DID method - maps to other methods (did:scid)
85    /// Format: <underlying_method>:<version>:<scid>
86    Scid {
87        /// Raw identifier for Display
88        identifier: String,
89        /// Underlying method type (e.g., "vh" for webvh)
90        underlying_method: String,
91        /// Version number
92        version: String,
93        /// Self-Certifying IDentifier
94        scid: String,
95    },
96    /// EBSI DID method for legal entities (did:ebsi)
97    /// Format: z<base58btc(0x01 + 16_random_bytes)>
98    Ebsi {
99        /// Raw identifier (e.g., "zfEmvX5twhXjQJiCWsukvQA")
100        identifier: String,
101    },
102    /// Catch-all for methods we don't explicitly model
103    Other {
104        /// Method name (e.g., "example")
105        method: String,
106        /// Raw identifier (opaque)
107        identifier: String,
108    },
109}
110
111impl DIDMethod {
112    /// Returns the method name ("key", "peer", "web", etc.)
113    pub fn name(&self) -> &str {
114        match self {
115            DIDMethod::Key { .. } => "key",
116            DIDMethod::Peer { .. } => "peer",
117            DIDMethod::Web { .. } => "web",
118            DIDMethod::Jwk { .. } => "jwk",
119            DIDMethod::Ethr { .. } => "ethr",
120            DIDMethod::Pkh { .. } => "pkh",
121            DIDMethod::Webvh { .. } => "webvh",
122            DIDMethod::Cheqd { .. } => "cheqd",
123            DIDMethod::Scid { .. } => "scid",
124            DIDMethod::Ebsi { .. } => "ebsi",
125            DIDMethod::Other { method, .. } => method,
126        }
127    }
128
129    /// Returns the raw method-specific identifier
130    pub fn identifier(&self) -> &str {
131        match self {
132            DIDMethod::Key { identifier, .. } => identifier,
133            DIDMethod::Peer { identifier, .. } => identifier,
134            DIDMethod::Web { identifier, .. } => identifier,
135            DIDMethod::Jwk { identifier, .. } => identifier,
136            DIDMethod::Ethr { identifier, .. } => identifier,
137            DIDMethod::Pkh { identifier, .. } => identifier,
138            DIDMethod::Webvh { identifier, .. } => identifier,
139            DIDMethod::Cheqd { identifier, .. } => identifier,
140            DIDMethod::Scid { identifier, .. } => identifier,
141            DIDMethod::Ebsi { identifier, .. } => identifier,
142            DIDMethod::Other { identifier, .. } => identifier,
143        }
144    }
145}
146
147impl fmt::Display for DIDMethod {
148    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
149        write!(f, "{}", self.name())
150    }
151}
152
153#[cfg(test)]
154mod tests {
155    use crate::DID;
156
157    #[test]
158    fn name_returns_correct_method() {
159        let cases = [
160            (
161                "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
162                "key",
163            ),
164            (
165                "did:peer:0z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
166                "peer",
167            ),
168            ("did:web:example.com", "web"),
169            (
170                "did:ethr:0xb9c5714089478a327f09197987f16f9e5d936e8a",
171                "ethr",
172            ),
173            (
174                "did:pkh:eip155:1:0xb9c5714089478a327f09197987f16f9e5d936e8a",
175                "pkh",
176            ),
177            ("did:example:custom123", "example"),
178        ];
179        for (did_str, expected_name) in cases {
180            let did: DID = did_str.parse().unwrap();
181            assert_eq!(did.method().name(), expected_name, "failed for {did_str}");
182        }
183    }
184
185    #[test]
186    fn identifier_returns_raw_id() {
187        let did: DID = "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"
188            .parse()
189            .unwrap();
190        assert_eq!(
191            did.method().identifier(),
192            "z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"
193        );
194    }
195
196    #[test]
197    fn display_matches_name() {
198        let did: DID = "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"
199            .parse()
200            .unwrap();
201        assert_eq!(format!("{}", did.method()), "key");
202    }
203
204    #[test]
205    fn display_other_method() {
206        let did: DID = "did:example:abc123".parse().unwrap();
207        assert_eq!(format!("{}", did.method()), "example");
208    }
209}