1#[cfg(feature = "alloc")]
2use alloc::string::String;
3#[cfg(feature = "alloc")]
4use alloc::string::ToString as _;
5use core::cmp::Ordering;
6use core::convert::TryFrom;
7use core::fmt::Debug;
8use core::fmt::Display;
9use core::fmt::Formatter;
10use core::fmt::Result as FmtResult;
11use core::hash::Hash;
12use core::hash::Hasher;
13use core::str::FromStr;
14
15use crate::core::Core;
16use crate::error::Error;
17use crate::error::Result;
18
19#[derive(Clone, Copy)]
20pub struct Inspect<'a>(&'a DID);
21
22impl Debug for Inspect<'_> {
23 fn fmt(&self, f: &mut Formatter) -> FmtResult {
24 f.debug_struct("DID")
25 .field("method", &self.0.method())
26 .field("method_id", &self.0.method_id())
27 .field("path", &self.0.path())
28 .field("query", &self.0.query())
29 .field("fragment", &self.0.fragment())
30 .finish()
31 }
32}
33
34#[derive(Clone)]
38#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
39#[cfg_attr(feature = "serde", serde(into = "String", try_from = "String"))]
40pub struct DID {
41 data: String,
42 core: Core,
43}
44
45impl DID {
46 pub const SCHEME: &'static str = "did";
48
49 pub fn parse(input: impl AsRef<str>) -> Result<Self> {
55 Ok(Self {
56 data: input.as_ref().to_string(),
57 core: Core::parse(input)?,
58 })
59 }
60
61 #[inline]
63 pub const fn inspect(&self) -> Inspect {
64 Inspect(self)
65 }
66
67 #[inline]
71 pub fn as_str(&self) -> &str {
72 &*self.data
73 }
74
75 #[cfg(feature = "alloc")]
77 #[inline]
78 pub fn into_string(self) -> String {
79 self.data
80 }
81
82 #[inline]
84 pub const fn scheme(&self) -> &'static str {
85 DID::SCHEME
86 }
87
88 #[inline]
90 pub fn authority(&self) -> &str {
91 self.core.authority(self.as_str())
92 }
93
94 #[inline]
96 pub fn method(&self) -> &str {
97 self.core.method(self.as_str())
98 }
99
100 #[inline]
102 pub fn method_id(&self) -> &str {
103 self.core.method_id(self.as_str())
104 }
105
106 #[inline]
108 pub fn path(&self) -> &str {
109 self.core.path(self.as_str())
110 }
111
112 #[inline]
114 pub fn query(&self) -> Option<&str> {
115 self.core.query(self.as_str())
116 }
117
118 #[inline]
120 pub fn fragment(&self) -> Option<&str> {
121 self.core.fragment(self.as_str())
122 }
123
124 #[inline]
126 pub fn query_pairs(&self) -> form_urlencoded::Parse {
127 self.core.query_pairs(self.as_str())
128 }
129
130 #[inline]
132 pub fn set_method(&mut self, value: impl AsRef<str>) {
133 self.core.set_method(&mut self.data, value.as_ref());
134 }
135
136 #[inline]
138 pub fn set_method_id(&mut self, value: impl AsRef<str>) {
139 self.core.set_method_id(&mut self.data, value.as_ref());
140 }
141
142 #[inline]
144 pub fn set_path(&mut self, value: impl AsRef<str>) {
145 self.core.set_path(&mut self.data, value.as_ref());
146 }
147
148 #[inline]
152 pub fn set_query(&mut self, value: Option<&str>) {
153 self.core.set_query(&mut self.data, value);
154 }
155
156 #[inline]
160 pub fn set_fragment(&mut self, value: Option<&str>) {
161 self.core.set_fragment(&mut self.data, value);
162 }
163
164 #[cfg(feature = "alloc")]
170 pub fn join(&self, other: impl AsRef<str>) -> Result<Self> {
171 let data: &str = other.as_ref();
172 let core: Core = Core::parse_relative(data)?;
173
174 resolution::transform_references(self, (data, &core))
175 }
176}
177
178impl Hash for DID {
179 fn hash<H>(&self, hasher: &mut H)
180 where
181 H: Hasher,
182 {
183 self.as_str().hash(hasher)
184 }
185}
186
187impl PartialEq for DID {
188 fn eq(&self, other: &Self) -> bool {
189 self.as_str() == other.as_str()
190 }
191}
192
193impl Eq for DID {}
194
195impl PartialOrd for DID {
196 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
197 self.as_str().partial_cmp(other.as_str())
198 }
199}
200
201impl Ord for DID {
202 fn cmp(&self, other: &Self) -> Ordering {
203 self.as_str().cmp(other.as_str())
204 }
205}
206
207impl PartialEq<str> for DID {
208 fn eq(&self, other: &str) -> bool {
209 self.as_str() == other
210 }
211}
212
213impl PartialEq<&'_ str> for DID {
214 fn eq(&self, other: &&'_ str) -> bool {
215 self == *other
216 }
217}
218
219impl Debug for DID {
220 fn fmt(&self, f: &mut Formatter) -> FmtResult {
221 f.write_fmt(format_args!("{:?}", self.as_str()))
222 }
223}
224
225impl Display for DID {
226 fn fmt(&self, f: &mut Formatter) -> FmtResult {
227 f.write_fmt(format_args!("{}", self.as_str()))
228 }
229}
230
231impl AsRef<str> for DID {
232 fn as_ref(&self) -> &str {
233 self.data.as_ref()
234 }
235}
236
237impl FromStr for DID {
238 type Err = Error;
239
240 fn from_str(string: &str) -> Result<Self, Self::Err> {
241 Self::parse(string)
242 }
243}
244
245#[cfg(feature = "alloc")]
246impl TryFrom<String> for DID {
247 type Error = Error;
248
249 fn try_from(other: String) -> Result<Self, Self::Error> {
250 Self::parse(other)
251 }
252}
253
254#[cfg(feature = "alloc")]
255impl From<DID> for String {
256 fn from(other: DID) -> Self {
257 other.into_string()
258 }
259}
260
261#[cfg(feature = "alloc")]
267mod resolution {
268 use alloc::borrow::Cow;
269 use core::fmt::Display;
270 use core::fmt::Formatter;
271 use core::fmt::Result as FmtResult;
272 use core::str::from_utf8_unchecked;
273
274 use crate::core::Core;
275 use crate::did::DID;
276 use crate::error::Error;
277 use crate::error::Result;
278
279 #[derive(Debug)]
280 #[repr(transparent)]
281 pub struct Path<'a>(Cow<'a, str>);
282
283 impl<'a> Path<'a> {
284 pub const fn new() -> Self {
285 Self(Cow::Borrowed(""))
286 }
287
288 pub fn push(&mut self, value: impl AsRef<[u8]>) {
289 self
290 .0
291 .to_mut()
292 .push_str(unsafe { from_utf8_unchecked(value.as_ref()) });
293 }
294
295 pub fn pop(&mut self) {
296 if self.0.is_empty() {
297 return;
298 }
299
300 if let Some(index) = self.0.rfind('/') {
301 self.0.to_mut().replace_range(index.., "");
302 }
303 }
304 }
305
306 impl<'a> From<Path<'a>> for Cow<'a, str> {
307 fn from(other: Path<'a>) -> Self {
308 other.0
309 }
310 }
311
312 impl Display for Path<'_> {
313 fn fmt(&self, f: &mut Formatter) -> FmtResult {
314 Display::fmt(&self.0, f)
315 }
316 }
317
318 #[allow(non_snake_case)]
324 pub fn transform_references(base: &DID, (data, core): (&str, &Core)) -> Result<DID> {
325 let P: &str = core.path(data);
326 let Q: Option<&str> = core.query(data);
327
328 let mut T: DID = base.clone();
329
330 if P.is_empty() {
331 T.set_path(base.path());
332 T.set_query(Q.or_else(|| base.query()));
333 } else {
334 if P.starts_with('/') {
335 T.set_path(remove_dot_segments(P));
336 } else {
337 T.set_path(remove_dot_segments(&merge_paths(base, P)?));
338 }
339
340 T.set_query(Q);
341 }
342
343 T.set_method(base.method()); T.set_method_id(base.method_id()); T.set_fragment(core.fragment(data));
346
347 Ok(T)
348 }
349
350 pub fn merge_paths<'a>(base: &'a DID, data: &'a str) -> Result<Cow<'a, str>> {
356 if base.method().is_empty() || base.method_id().is_empty() {
361 return Err(Error::InvalidAuthority);
362 }
363
364 if base.path().is_empty() {
369 return Ok(data.into());
370 }
371
372 let mut path: &str = base.path();
379
380 if let Some(index) = path.rfind('/') {
381 path = &path[..=index];
382 }
383
384 Ok([path, data].join("").into())
385 }
386
387 pub fn remove_dot_segments(path: &str) -> Cow<str> {
391 fn next_segment(input: impl AsRef<[u8]>) -> Option<usize> {
392 match input.as_ref() {
393 [b'/', input @ ..] => next_segment(input).map(|index| index + 1),
394 input => input.iter().position(|byte| *byte == b'/'),
395 }
396 }
397
398 let mut output: Path = Path::new();
399 let mut input: &[u8] = path.as_bytes();
400
401 loop {
402 match input {
403 [b'.', b'.', b'/', ..] => {
405 input = &input[3..];
406 }
407 [b'.', b'/', ..] => {
409 input = &input[2..];
410 }
411 [b'/', b'.', b'/', ..] => {
413 input = &input[2..];
414 }
415 [b'/', b'.'] => {
417 input = &input[..1];
418 }
419 [b'/', b'.', b'.', b'/', ..] => {
421 input = &input[3..];
422 output.pop();
423 }
424 [b'/', b'.', b'.'] => {
426 input = &input[..2];
427 output.pop();
428 }
429 [b'.'] => {
431 input = &input[1..];
432 }
433 [b'.', b'.'] => {
435 input = &input[2..];
436 }
437 _ => {
438 if let Some(index) = next_segment(input) {
439 output.push(&input[..index]);
440 input = &input[index..];
441 } else {
442 output.push(input);
443 break;
444 }
445 }
446 }
447 }
448
449 output.into()
450 }
451}