1#[derive(Debug, Clone, PartialEq, Eq)]
7pub struct DidUrl {
8 pub did: String,
10 pub method: String,
12 pub method_id: String,
14 pub path: Option<String>,
16 pub query: Option<String>,
18 pub fragment: Option<String>,
20}
21
22impl DidUrl {
23 pub fn parse(input: &str) -> baseid_core::Result<Self> {
25 let without_fragment = if let Some((base, fragment)) = input.split_once('#') {
26 let fragment = Some(fragment.to_string());
27 (base, fragment)
28 } else {
29 (input, None)
30 };
31
32 let (base, fragment) = without_fragment;
33
34 let (base, query) = if let Some((b, q)) = base.split_once('?') {
35 (b, Some(q.to_string()))
36 } else {
37 (base, None)
38 };
39
40 let (did_part, path) = if let Some(idx) = base.find('/') {
41 (&base[..idx], Some(base[idx..].to_string()))
42 } else {
43 (base, None)
44 };
45
46 let parts: Vec<&str> = did_part.splitn(3, ':').collect();
47 if parts.len() < 3 || parts[0] != "did" {
48 return Err(baseid_core::error::DidError::InvalidDid.into());
49 }
50
51 Ok(Self {
52 did: did_part.to_string(),
53 method: parts[1].to_string(),
54 method_id: parts[2].to_string(),
55 path,
56 query,
57 fragment,
58 })
59 }
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65
66 #[test]
67 fn parse_simple_did() {
68 let url =
69 DidUrl::parse("did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK").unwrap();
70 assert_eq!(url.method, "key");
71 assert_eq!(
72 url.method_id,
73 "z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"
74 );
75 assert!(url.path.is_none());
76 assert!(url.query.is_none());
77 assert!(url.fragment.is_none());
78 }
79
80 #[test]
81 fn parse_did_with_fragment() {
82 let url = DidUrl::parse("did:web:example.com#key-1").unwrap();
83 assert_eq!(url.method, "web");
84 assert_eq!(url.method_id, "example.com");
85 assert_eq!(url.fragment.as_deref(), Some("key-1"));
86 }
87
88 #[test]
89 fn reject_invalid_did() {
90 assert!(DidUrl::parse("not-a-did").is_err());
91 assert!(DidUrl::parse("did:").is_err());
92 }
93}