identity_document/service/
service_endpoint.rs1use core::fmt::Display;
5use core::fmt::Formatter;
6
7use indexmap::map::IndexMap;
8use serde::Serialize;
9
10use identity_core::common::OrderedSet;
11use identity_core::common::Url;
12use identity_core::convert::FmtJson;
13
14#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
18#[serde(untagged)]
19pub enum ServiceEndpoint {
20 One(Url),
21 Set(OrderedSet<Url>),
22 Map(IndexMap<String, OrderedSet<Url>>),
23}
24
25impl From<Url> for ServiceEndpoint {
26 fn from(url: Url) -> Self {
27 ServiceEndpoint::One(url)
28 }
29}
30
31impl From<OrderedSet<Url>> for ServiceEndpoint {
32 fn from(set: OrderedSet<Url>) -> Self {
33 ServiceEndpoint::Set(set)
34 }
35}
36
37impl From<IndexMap<String, OrderedSet<Url>>> for ServiceEndpoint {
38 fn from(map: IndexMap<String, OrderedSet<Url>>) -> Self {
39 ServiceEndpoint::Map(map)
40 }
41}
42
43impl Display for ServiceEndpoint {
44 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
45 self.fmt_json(f)
46 }
47}
48
49#[cfg(test)]
50mod tests {
51 use identity_core::convert::FromJson;
52 use identity_core::convert::ToJson;
53
54 use super::*;
55
56 #[test]
57 fn test_service_endpoint_one() {
58 let url1 = Url::parse("https://iota.org/").unwrap();
59 let url2 = Url::parse("wss://www.example.com/socketserver/").unwrap();
60 let url3 = Url::parse("did:abc:123#service").unwrap();
61
62 let endpoint1: ServiceEndpoint = ServiceEndpoint::One(url1);
64 let ser_endpoint1: String = endpoint1.to_json().unwrap();
65 assert_eq!(ser_endpoint1, "\"https://iota.org/\"");
66 assert_eq!(endpoint1, ServiceEndpoint::from_json(&ser_endpoint1).unwrap());
67
68 let endpoint2: ServiceEndpoint = ServiceEndpoint::One(url2);
69 let ser_endpoint2: String = endpoint2.to_json().unwrap();
70 assert_eq!(ser_endpoint2, "\"wss://www.example.com/socketserver/\"");
71 assert_eq!(endpoint2, ServiceEndpoint::from_json(&ser_endpoint2).unwrap());
72
73 let endpoint3: ServiceEndpoint = ServiceEndpoint::One(url3);
74 let ser_endpoint3: String = endpoint3.to_json().unwrap();
75 assert_eq!(ser_endpoint3, "\"did:abc:123#service\"");
76 assert_eq!(endpoint3, ServiceEndpoint::from_json(&ser_endpoint3).unwrap());
77 }
78
79 #[test]
80 fn test_service_endpoint_set() {
81 let url1 = Url::parse("https://iota.org/").unwrap();
82 let url2 = Url::parse("wss://www.example.com/socketserver/").unwrap();
83 let url3 = Url::parse("did:abc:123#service").unwrap();
84
85 let mut set: OrderedSet<Url> = OrderedSet::new();
87 assert!(set.append(url1.clone()));
89 let endpoint_set: ServiceEndpoint = ServiceEndpoint::Set(set.clone());
90 let ser_endpoint_set: String = endpoint_set.to_json().unwrap();
91 assert_eq!(ser_endpoint_set, "[\"https://iota.org/\"]");
92 assert_eq!(endpoint_set, ServiceEndpoint::from_json(&ser_endpoint_set).unwrap());
93 assert!(set.append(url2.clone()));
95 let endpoint_set: ServiceEndpoint = ServiceEndpoint::Set(set.clone());
96 let ser_endpoint_set: String = endpoint_set.to_json().unwrap();
97 assert_eq!(
98 ser_endpoint_set,
99 "[\"https://iota.org/\",\"wss://www.example.com/socketserver/\"]"
100 );
101 assert_eq!(endpoint_set, ServiceEndpoint::from_json(&ser_endpoint_set).unwrap());
102 assert!(set.append(url3.clone()));
104 let endpoint_set: ServiceEndpoint = ServiceEndpoint::Set(set.clone());
105 let ser_endpoint_set: String = endpoint_set.to_json().unwrap();
106 assert_eq!(
107 ser_endpoint_set,
108 "[\"https://iota.org/\",\"wss://www.example.com/socketserver/\",\"did:abc:123#service\"]"
109 );
110 assert_eq!(endpoint_set, ServiceEndpoint::from_json(&ser_endpoint_set).unwrap());
111
112 let mut duplicates_set: OrderedSet<Url> = OrderedSet::new();
114 duplicates_set.append(url1.clone());
115 duplicates_set.append(url1.clone());
116 assert_eq!(
117 ServiceEndpoint::Set(duplicates_set.clone()).to_json().unwrap(),
118 "[\"https://iota.org/\"]"
119 );
120 duplicates_set.append(url2.clone());
121 duplicates_set.append(url2.clone());
122 duplicates_set.append(url1.clone());
123 assert_eq!(
124 ServiceEndpoint::Set(duplicates_set.clone()).to_json().unwrap(),
125 "[\"https://iota.org/\",\"wss://www.example.com/socketserver/\"]"
126 );
127 assert!(duplicates_set.append(url3.clone()));
128 duplicates_set.append(url3);
129 duplicates_set.append(url1);
130 duplicates_set.append(url2);
131 assert_eq!(
132 ServiceEndpoint::Set(duplicates_set.clone()).to_json().unwrap(),
133 "[\"https://iota.org/\",\"wss://www.example.com/socketserver/\",\"did:abc:123#service\"]"
134 );
135 }
136
137 #[test]
138 fn test_service_endpoint_map() {
139 let url1 = Url::parse("https://iota.org/").unwrap();
140 let url2 = Url::parse("wss://www.example.com/socketserver/").unwrap();
141 let url3 = Url::parse("did:abc:123#service").unwrap();
142 let url4 = Url::parse("did:xyz:789#link").unwrap();
143
144 let mut map: IndexMap<String, OrderedSet<Url>> = IndexMap::new();
146 assert!(map
148 .insert("key".to_owned(), OrderedSet::try_from(vec![url1]).unwrap())
149 .is_none());
150 let endpoint_map: ServiceEndpoint = ServiceEndpoint::Map(map.clone());
151 let ser_endpoint_map: String = endpoint_map.to_json().unwrap();
152 assert_eq!(ser_endpoint_map, r#"{"key":["https://iota.org/"]}"#);
153 assert_eq!(endpoint_map, ServiceEndpoint::from_json(&ser_endpoint_map).unwrap());
154 assert!(map
156 .insert("apple".to_owned(), OrderedSet::try_from(vec![url2]).unwrap())
157 .is_none());
158 let endpoint_map: ServiceEndpoint = ServiceEndpoint::Map(map.clone());
159 let ser_endpoint_map: String = endpoint_map.to_json().unwrap();
160 assert_eq!(
161 ser_endpoint_map,
162 r#"{"key":["https://iota.org/"],"apple":["wss://www.example.com/socketserver/"]}"#
163 );
164 assert_eq!(endpoint_map, ServiceEndpoint::from_json(&ser_endpoint_map).unwrap());
165 assert!(map
167 .insert("example".to_owned(), OrderedSet::try_from(vec![url3]).unwrap())
168 .is_none());
169 let endpoint_map: ServiceEndpoint = ServiceEndpoint::Map(map.clone());
170 let ser_endpoint_map: String = endpoint_map.to_json().unwrap();
171 assert_eq!(
172 ser_endpoint_map,
173 r#"{"key":["https://iota.org/"],"apple":["wss://www.example.com/socketserver/"],"example":["did:abc:123#service"]}"#
174 );
175 assert_eq!(endpoint_map, ServiceEndpoint::from_json(&ser_endpoint_map).unwrap());
176
177 map.shift_remove("key"); assert!(map
181 .insert("bee".to_owned(), OrderedSet::try_from(vec![url4]).unwrap())
182 .is_none());
183 let endpoint_map: ServiceEndpoint = ServiceEndpoint::Map(map.clone());
184 let ser_endpoint_map: String = endpoint_map.to_json().unwrap();
185 assert_eq!(
186 ser_endpoint_map,
187 r#"{"apple":["wss://www.example.com/socketserver/"],"example":["did:abc:123#service"],"bee":["did:xyz:789#link"]}"#
188 );
189 assert_eq!(endpoint_map, ServiceEndpoint::from_json(&ser_endpoint_map).unwrap());
190 }
191
192 #[test]
193 fn test_service_endpoint_serde_fails() {
194 assert!(ServiceEndpoint::from_json("").is_err());
196 assert!(ServiceEndpoint::from_json("\"\"").is_err());
197
198 assert!(ServiceEndpoint::from_json("\" \"").is_err());
200 assert!(ServiceEndpoint::from_json("\"\t\"").is_err());
201 assert!(ServiceEndpoint::from_json(r#""https:// iota.org/""#).is_err());
202 assert!(ServiceEndpoint::from_json(r#"["https://iota.org/","wss://www.example.com /socketserver/"]"#).is_err());
203 assert!(ServiceEndpoint::from_json(r#"{"key":["https:// iota.org/"],"apple":["wss://www.example.com/socketserver/"],"example":["did:abc:123#service"]}"#).is_err());
204
205 assert!(ServiceEndpoint::from_json(r#"["https://iota.org/","https://iota.org/"]"#).is_err());
207 assert!(ServiceEndpoint::from_json(r#"["https://iota.org/a/./b/../b/.","https://iota.org/a/b/"]"#).is_err());
209
210 assert!(ServiceEndpoint::from_json(r#"{["https://iota.org/"],["wss://www.example.com/socketserver/"]}"#).is_err());
212 assert!(
213 ServiceEndpoint::from_json(r#"{"key1":["https://iota.org/"],["wss://www.example.com/socketserver/"]}"#).is_err()
214 );
215 assert!(
216 ServiceEndpoint::from_json(r#"{["https://iota.org/"],"key2":["wss://www.example.com/socketserver/"]}"#).is_err()
217 );
218 }
219}