identity_document/utils/
did_url_query.rs1use std::borrow::Cow;
5
6use identity_did::CoreDID;
7use identity_did::DIDUrl;
8use identity_did::RelativeDIDUrl;
9use identity_did::DID;
10
11#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
13#[repr(transparent)]
14pub struct DIDUrlQuery<'query>(Cow<'query, str>);
15
16impl DIDUrlQuery<'_> {
17 pub(crate) fn matches(&self, did_url: &DIDUrl) -> bool {
19 if let Some(did_str) = self.did_str() {
21 if did_str != did_url.did().as_str() {
22 return false;
23 }
24 }
25
26 match self.fragment().zip(did_url.fragment()) {
28 Some((a, b)) => a == b,
29 None => false,
30 }
31 }
32
33 fn did_str(&self) -> Option<&str> {
35 let query: &str = self.0.as_ref();
36 if !query.starts_with(CoreDID::SCHEME) {
37 return None;
38 }
39
40 let mut end_pos: usize = query.len();
43 end_pos = end_pos.min(query.find('?').unwrap_or(end_pos));
44 end_pos = end_pos.min(query.find('/').unwrap_or(end_pos));
45 end_pos = end_pos.min(query.find('#').unwrap_or(end_pos));
46 query.get(0..end_pos)
47 }
48
49 fn fragment(&self) -> Option<&str> {
51 let query: &str = self.0.as_ref();
52 let fragment_maybe: Option<&str> = if query.starts_with(CoreDID::SCHEME) {
53 query.rfind('#').and_then(|index| query.get(index + 1..))
55 } else if let Some(fragment_delimiter_index) = query.rfind('#') {
56 query.get(fragment_delimiter_index + 1..)
58 } else {
59 Some(query)
61 };
62 fragment_maybe.filter(|fragment| !fragment.is_empty())
63 }
64}
65
66impl<'query> From<&'query str> for DIDUrlQuery<'query> {
67 fn from(other: &'query str) -> Self {
68 Self(Cow::Borrowed(other))
69 }
70}
71
72impl<'query> From<&'query String> for DIDUrlQuery<'query> {
73 fn from(other: &'query String) -> Self {
74 Self(Cow::Borrowed(&**other))
75 }
76}
77
78impl<'query> From<&'query DIDUrl> for DIDUrlQuery<'query> {
79 fn from(other: &'query DIDUrl) -> Self {
80 Self(Cow::Owned(other.to_string()))
81 }
82}
83
84impl From<DIDUrl> for DIDUrlQuery<'_> {
85 fn from(other: DIDUrl) -> Self {
86 Self(Cow::Owned(other.to_string()))
87 }
88}
89
90impl<'query> From<&'query RelativeDIDUrl> for DIDUrlQuery<'query> {
91 fn from(other: &'query RelativeDIDUrl) -> Self {
92 Self(Cow::Owned(other.to_string()))
93 }
94}
95
96#[cfg(test)]
97mod tests {
98 use identity_did::DIDUrl;
99 use std::ops::Not;
100
101 use super::*;
102
103 #[test]
104 fn test_did_str() {
105 assert!(DIDUrlQuery::from("").did_str().is_none());
106 assert!(DIDUrlQuery::from("fragment").did_str().is_none());
107 assert!(DIDUrlQuery::from("#fragment").did_str().is_none());
108 assert!(DIDUrlQuery::from("?query").did_str().is_none());
109 assert!(DIDUrlQuery::from("/path").did_str().is_none());
110 assert!(DIDUrlQuery::from("/path?query#fragment").did_str().is_none());
111 assert!(DIDUrlQuery::from("method:missingscheme123").did_str().is_none());
112 assert!(DIDUrlQuery::from("iota:example").did_str().is_none());
113 assert_eq!(
114 DIDUrlQuery::from("did:iota:example").did_str(),
115 Some("did:iota:example")
116 );
117 assert_eq!(
118 DIDUrlQuery::from("did:iota:example#fragment").did_str(),
119 Some("did:iota:example")
120 );
121 assert_eq!(
122 DIDUrlQuery::from("did:iota:example?query").did_str(),
123 Some("did:iota:example")
124 );
125 assert_eq!(
126 DIDUrlQuery::from("did:iota:example/path").did_str(),
127 Some("did:iota:example")
128 );
129 assert_eq!(
130 DIDUrlQuery::from("did:iota:example/path?query#fragment").did_str(),
131 Some("did:iota:example")
132 );
133 assert_eq!(
134 DIDUrlQuery::from("did:iota:example/path?query&relativeRef=/#fragment").did_str(),
135 Some("did:iota:example")
136 );
137 }
138
139 #[test]
140 fn test_fragment() {
141 assert!(DIDUrlQuery::from("").fragment().is_none());
142 assert_eq!(DIDUrlQuery::from("fragment").fragment(), Some("fragment"));
143 assert_eq!(DIDUrlQuery::from("#fragment").fragment(), Some("fragment"));
144 assert_eq!(DIDUrlQuery::from("/path?query#fragment").fragment(), Some("fragment"));
145 assert!(DIDUrlQuery::from("did:iota:example").fragment().is_none());
146 assert_eq!(
147 DIDUrlQuery::from("did:iota:example#fragment").fragment(),
148 Some("fragment")
149 );
150 assert!(DIDUrlQuery::from("did:iota:example?query").fragment().is_none());
151 assert!(DIDUrlQuery::from("did:iota:example/path").fragment().is_none());
152 assert_eq!(
153 DIDUrlQuery::from("did:iota:example/path?query#fragment").fragment(),
154 Some("fragment")
155 );
156 assert_eq!(
157 DIDUrlQuery::from("did:iota:example/path?query&relativeRef=/#fragment").fragment(),
158 Some("fragment")
159 );
160 }
161
162 #[test]
163 fn test_matches() {
164 let did_base: DIDUrl = DIDUrl::parse("did:iota:example").unwrap();
165 let did_path: DIDUrl = DIDUrl::parse("did:iota:example/path").unwrap();
166 let did_query: DIDUrl = DIDUrl::parse("did:iota:example?query").unwrap();
167 let did_fragment: DIDUrl = DIDUrl::parse("did:iota:example#fragment").unwrap();
168 let did_different_fragment: DIDUrl = DIDUrl::parse("did:iota:example#differentfragment").unwrap();
169 let did_url: DIDUrl = DIDUrl::parse("did:iota:example/path?query#fragment").unwrap();
170 let did_url_complex: DIDUrl = DIDUrl::parse("did:iota:example/path?query&relativeRef=/#fragment").unwrap();
171
172 {
174 let query_empty = DIDUrlQuery::from("");
175 assert!(query_empty.matches(&did_base).not());
176 assert!(query_empty.matches(&did_path).not());
177 assert!(query_empty.matches(&did_query).not());
178 assert!(query_empty.matches(&did_fragment).not());
179 assert!(query_empty.matches(&did_different_fragment).not());
180 assert!(query_empty.matches(&did_url).not());
181 assert!(query_empty.matches(&did_url_complex).not());
182 }
183
184 {
186 let query_fragment_only = DIDUrlQuery::from("fragment");
187 assert!(query_fragment_only.matches(&did_base).not());
188 assert!(query_fragment_only.matches(&did_path).not());
189 assert!(query_fragment_only.matches(&did_query).not());
190 assert!(query_fragment_only.matches(&did_fragment));
191 assert!(query_fragment_only.matches(&did_different_fragment).not());
192 assert!(query_fragment_only.matches(&did_url));
193 assert!(query_fragment_only.matches(&did_url_complex));
194 }
195
196 {
198 let query_different_fragment = DIDUrlQuery::from("differentfragment");
199 assert!(query_different_fragment.matches(&did_base).not());
200 assert!(query_different_fragment.matches(&did_path).not());
201 assert!(query_different_fragment.matches(&did_query).not());
202 assert!(query_different_fragment.matches(&did_fragment).not());
203 assert!(query_different_fragment.matches(&did_different_fragment));
204 assert!(query_different_fragment.matches(&did_url).not());
205 assert!(query_different_fragment.matches(&did_url_complex).not());
206 }
207
208 {
210 let query_fragment_delimiter = DIDUrlQuery::from("#fragment");
211 assert!(query_fragment_delimiter.matches(&did_base).not());
212 assert!(query_fragment_delimiter.matches(&did_path).not());
213 assert!(query_fragment_delimiter.matches(&did_query).not());
214 assert!(query_fragment_delimiter.matches(&did_fragment));
215 assert!(query_fragment_delimiter.matches(&did_different_fragment).not());
216 assert!(query_fragment_delimiter.matches(&did_url));
217 assert!(query_fragment_delimiter.matches(&did_url_complex));
218 }
219
220 {
222 let query_relative_did_url = DIDUrlQuery::from("/path?query#fragment");
223 assert!(query_relative_did_url.matches(&did_base).not());
224 assert!(query_relative_did_url.matches(&did_path).not());
225 assert!(query_relative_did_url.matches(&did_query).not());
226 assert!(query_relative_did_url.matches(&did_fragment));
227 assert!(query_relative_did_url.matches(&did_different_fragment).not());
228 assert!(query_relative_did_url.matches(&did_url));
229 assert!(query_relative_did_url.matches(&did_url_complex));
230 }
231
232 {
234 let query_did = DIDUrlQuery::from("did:iota:example");
235 assert!(query_did.matches(&did_base).not());
236 assert!(query_did.matches(&did_path).not());
237 assert!(query_did.matches(&did_query).not());
238 assert!(query_did.matches(&did_fragment).not());
239 assert!(query_did.matches(&did_different_fragment).not());
240 assert!(query_did.matches(&did_url).not());
241 assert!(query_did.matches(&did_url_complex).not());
242 }
243
244 {
246 let query_did_fragment = DIDUrlQuery::from("did:iota:example#fragment");
247 assert!(query_did_fragment.matches(&did_base).not());
248 assert!(query_did_fragment.matches(&did_path).not());
249 assert!(query_did_fragment.matches(&did_query).not());
250 assert!(query_did_fragment.matches(&did_fragment));
251 assert!(query_did_fragment.matches(&did_different_fragment).not());
252 assert!(query_did_fragment.matches(&did_url));
253 assert!(query_did_fragment.matches(&did_url_complex));
254 }
255
256 {
258 let query_did_fragment = DIDUrlQuery::from("did:iota:example/path?query#fragment");
259 assert!(query_did_fragment.matches(&did_base).not());
260 assert!(query_did_fragment.matches(&did_path).not());
261 assert!(query_did_fragment.matches(&did_query).not());
262 assert!(query_did_fragment.matches(&did_fragment));
263 assert!(query_did_fragment.matches(&did_different_fragment).not());
264 assert!(query_did_fragment.matches(&did_url));
265 assert!(query_did_fragment.matches(&did_url_complex));
266 }
267
268 {
270 let query_did_fragment = DIDUrlQuery::from("did:iota:example/path?query&relativeRef=/#fragment");
271 assert!(query_did_fragment.matches(&did_base).not());
272 assert!(query_did_fragment.matches(&did_path).not());
273 assert!(query_did_fragment.matches(&did_query).not());
274 assert!(query_did_fragment.matches(&did_fragment));
275 assert!(query_did_fragment.matches(&did_different_fragment).not());
276 assert!(query_did_fragment.matches(&did_url));
277 assert!(query_did_fragment.matches(&did_url_complex));
278 }
279 }
280}