1use crate::{
2 string::{method_id_encoded, url_encoded, validate_method_name},
3 url::{URLParameters, URL},
4};
5use anyhow::anyhow;
6use serde::{de::Visitor, Deserialize, Serialize};
7use std::fmt::Display;
8
9#[derive(Clone, Hash, Default, Debug, PartialOrd, Ord, Eq, PartialEq)]
35pub struct DID {
36 pub name: Vec<u8>,
37 pub id: Vec<u8>,
38}
39
40impl Serialize for DID {
41 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
42 where
43 S: serde::Serializer,
44 {
45 serializer.serialize_str(&self.to_string())
46 }
47}
48
49struct DIDVisitor;
50impl Visitor<'_> for DIDVisitor {
51 type Value = DID;
52
53 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
54 formatter.write_str("Expecting a decentralized identity")
55 }
56
57 fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
58 where
59 E: serde::de::Error,
60 {
61 match DID::parse(&v) {
62 Ok(did) => Ok(did),
63 Err(e) => Err(E::custom(e)),
64 }
65 }
66
67 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
68 where
69 E: serde::de::Error,
70 {
71 match DID::parse(v) {
72 Ok(did) => Ok(did),
73 Err(e) => Err(E::custom(e)),
74 }
75 }
76}
77
78impl<'de> Deserialize<'de> for DID {
79 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
80 where
81 D: serde::Deserializer<'de>,
82 {
83 deserializer.deserialize_any(DIDVisitor)
84 }
85}
86
87impl Display for DID {
88 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89 let mut ret = String::from("did:");
90
91 ret += &url_encoded(&self.name);
92 ret += &(":".to_string() + &method_id_encoded(&self.id));
93 f.write_str(&ret)
94 }
95}
96
97impl DID {
98 pub fn parse(s: &str) -> Result<Self, anyhow::Error> {
100 match s.strip_prefix("did:") {
101 Some(s) => match s.split_once(':') {
102 Some((method_name, method_id)) => {
103 if method_id.is_empty() {
104 return Err(anyhow!("Method ID cannot be empty"));
105 }
106
107 if method_name.is_empty() {
108 return Err(anyhow!("Method name cannot be empty"));
109 }
110
111 validate_method_name(method_name.as_bytes())?;
112 Ok(DID {
113 name: method_name.into(),
114 id: method_id.into(),
115 })
116 }
117 None => Err(anyhow!("DID is missing method_id")),
118 },
119 None => Err(anyhow!("DID is missing method_name, method_id")),
120 }
121 }
122
123 pub fn join(&self, parameters: URLParameters) -> URL {
137 URL {
138 did: self.clone(),
139 parameters: Some(parameters),
140 }
141 }
142}
143
144mod tests {
145 #[test]
146 fn test_to_string() {
147 use super::DID;
148
149 let did = DID {
150 name: "abcdef".into(),
151 id: "123456".into(),
152 ..Default::default()
153 };
154
155 assert_eq!(did.to_string(), "did:abcdef:123456");
156
157 let did = DID {
158 name: "abcdef".into(),
159 id: "123456:u:alice".into(),
160 ..Default::default()
161 };
162
163 assert_eq!(did.to_string(), "did:abcdef:123456:u:alice");
164 }
165
166 #[test]
167 fn test_parse() {
168 use super::DID;
169
170 assert!(DID::parse("").is_err());
171 assert!(DID::parse("did::").is_err());
172 assert!(DID::parse("did:a:").is_err());
173 assert!(DID::parse("frobnik").is_err());
174 assert!(DID::parse("did").is_err());
175 assert!(DID::parse("frobnik:").is_err());
176 assert!(DID::parse("did:").is_err());
177 assert!(DID::parse("did:abcdef").is_err());
178
179 let did = DID::parse("did:abcdef:123456").unwrap();
180 assert_eq!(
181 did,
182 DID {
183 name: "abcdef".into(),
184 id: "123456".into(),
185 ..Default::default()
186 }
187 );
188
189 let did = DID::parse("did:abcdef:123456:u:alice").unwrap();
190 assert_eq!(
191 did,
192 DID {
193 name: "abcdef".into(),
194 id: "123456:u:alice".into(),
195 ..Default::default()
196 }
197 );
198 }
199
200 #[test]
201 fn test_serde() {
202 use super::DID;
203
204 let did: [DID; 1] = serde_json::from_str(r#"["did:123456:123"]"#).unwrap();
205 assert_eq!(
206 did[0],
207 DID {
208 name: "123456".into(),
209 id: "123".into(),
210 ..Default::default()
211 }
212 );
213
214 assert_eq!(
215 serde_json::to_string(&did).unwrap(),
216 r#"["did:123456:123"]"#
217 );
218 }
219}