1#![allow(clippy::missing_panics_doc)]
25#![cfg_attr(not(feature = "std"), no_std)]
26#![cfg_attr(docsrs, feature(doc_cfg))]
27#[cfg(not(feature = "std"))]
28extern crate alloc;
29#[cfg(all(not(feature = "std"), feature = "alloc"))]
30use alloc::{borrow::ToOwned, string::String, vec::Vec};
31#[cfg(feature = "alloc")]
32use core::str::FromStr;
33use core::{
34 cmp::Ordering,
35 convert::{TryFrom, TryInto},
36 fmt,
37 hash::{self, Hash},
38 num::{NonZeroU8, NonZeroU32},
39 ops::Range,
40};
41
42#[cfg(feature = "std")]
43use std::borrow::ToOwned;
44
45mod cow;
46use cow::TriCow;
47
48mod tables;
49
50#[cfg(feature = "alloc")]
51mod owned;
52#[cfg(feature = "alloc")]
53#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
54pub use owned::Urn;
55
56#[cfg(feature = "alloc")]
57#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
58pub mod percent;
59#[cfg(not(feature = "alloc"))]
60mod percent;
61use percent::{
62 normalize_range,
63 parse_f_component,
64 parse_nss,
65 parse_q_component,
66 parse_r_component,
67 validate_f_component,
68 validate_nss,
69 validate_q_component,
70 validate_r_component,
71};
72
73#[cfg(feature = "serde")]
74mod serde;
75
76fn is_valid_nid(s: &str) -> bool {
78 check_nid(s).is_ok()
79}
80
81fn check_nid(s: &str) -> Result<bool> {
84 let bytes = s.as_bytes();
85 if bytes.len() < 2 || bytes.len() > 32 || bytes[0] == b'-' {
86 return Err(Error::InvalidNid);
87 }
88 let mut has_upper = false;
89 for &b in bytes {
90 if tables::BYTE_CLASS[b as usize] & tables::NID == 0 {
91 return Err(Error::InvalidNid);
92 }
93 has_upper |= b.is_ascii_uppercase();
94 }
95 Ok(has_upper)
96}
97
98const URN_PREFIX: &str = "urn:";
99const NID_NSS_SEPARATOR: &str = ":";
100const RCOMP_PREFIX: &str = "?+";
101const QCOMP_PREFIX: &str = "?=";
102const FCOMP_PREFIX: &str = "#";
103
104fn parse_urn(mut s: TriCow) -> Result<UrnSlice> {
105 if !s.is_char_boundary(URN_PREFIX.len()) {
107 return Err(Error::InvalidScheme);
108 }
109
110 s.make_lowercase(..URN_PREFIX.len())?;
111
112 if &s[..URN_PREFIX.len()] != URN_PREFIX {
113 return Err(Error::InvalidScheme);
114 }
115
116 let nid_start = URN_PREFIX.len();
117 let nid_end = nid_start
118 + s[nid_start..].find(NID_NSS_SEPARATOR).ok_or_else(|| {
119 if is_valid_nid(&s[nid_start..]) {
120 Error::InvalidNss
122 } else {
123 Error::InvalidNid
125 }
126 })?;
127
128 if !is_valid_nid(&s[nid_start..nid_end]) {
129 return Err(Error::InvalidNid);
130 }
131
132 s.make_lowercase(nid_start..nid_end)?;
134
135 let nss_start = nid_end + NID_NSS_SEPARATOR.len();
136 let nss_end = parse_nss(&mut s, nss_start)?;
137
138 if nss_end == nss_start {
140 return Err(Error::InvalidNss);
141 }
142
143 let mut end = nss_end;
144 let mut last_component_error = Error::InvalidNss;
145
146 let r_component_len = if s[end..].starts_with(RCOMP_PREFIX) {
147 let rc_start = end + RCOMP_PREFIX.len();
148 end = parse_r_component(&mut s, rc_start)?;
149 last_component_error = Error::InvalidRComponent;
150 Some((end - rc_start).try_into().ok().and_then(NonZeroU32::new).ok_or(last_component_error)?)
151 } else {
152 None
153 };
154
155 let q_component_len = if s[end..].starts_with(QCOMP_PREFIX) {
156 let qc_start = end + QCOMP_PREFIX.len();
157 end = parse_q_component(&mut s, qc_start)?;
158 last_component_error = Error::InvalidQComponent;
159 Some((end - qc_start).try_into().ok().and_then(NonZeroU32::new).ok_or(last_component_error)?)
160 } else {
161 None
162 };
163
164 if s[end..].starts_with(FCOMP_PREFIX) {
165 let fc_start = end + FCOMP_PREFIX.len();
166 end = parse_f_component(&mut s, fc_start)?;
167 last_component_error = Error::InvalidFComponent;
168 }
169
170 if end < s.len() {
171 return Err(last_component_error);
172 }
173
174 let nid_len = u8::try_from(nid_end - nid_start).ok().and_then(NonZeroU8::new).ok_or(Error::InvalidNid)?;
176 let nss_len = u32::try_from(nss_end - nss_start).ok().and_then(NonZeroU32::new).ok_or(Error::InvalidNss)?;
178 Ok(UrnSlice {
179 urn: s,
180 nid_len,
181 nss_len,
182 r_component_len,
183 q_component_len,
184 })
185}
186
187#[non_exhaustive]
189#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
190pub enum Error {
191 #[error("invalid urn scheme")]
193 InvalidScheme,
194 #[error("invalid urn nid (namespace id)")]
196 InvalidNid,
197 #[error("invalid urn nss (namespace-specific string)")]
199 InvalidNss,
200 #[error("invalid urn r-component")]
202 InvalidRComponent,
203 #[error("invalid urn q-component")]
205 InvalidQComponent,
206 #[error("invalid urn f-component (fragment)")]
208 InvalidFComponent,
209 #[error("an allocation was required, but not possible")]
212 AllocRequired,
213 #[error("urn contains invalid utf-8")]
215 InvalidUtf8,
216}
217
218type Result<T, E = Error> = core::result::Result<T, E>;
219
220pub struct UrnSlice<'a> {
254 urn: TriCow<'a>,
256 nid_len: NonZeroU8,
257 nss_len: NonZeroU32,
258 r_component_len: Option<NonZeroU32>,
259 q_component_len: Option<NonZeroU32>,
260}
261
262impl UrnSlice<'static> {
263 pub fn from_static(s: &'static str) -> Result<UrnSlice<'static>> {
278 parse_urn(TriCow::Borrowed(s))
279 }
280}
281
282impl<'a> UrnSlice<'a> {
283 #[inline]
284 const fn nid_range(&self) -> Range<usize> {
285 let start = URN_PREFIX.len();
287 start..start + self.nid_len.get() as usize
288 }
289
290 #[inline]
291 const fn nss_range(&self) -> Range<usize> {
292 let start = self.nid_range().end + NID_NSS_SEPARATOR.len();
294 start..start + self.nss_len.get() as usize
295 }
296
297 #[inline]
298 fn r_component_range(&self) -> Option<Range<usize>> {
299 self.r_component_len.map(|r_component_len| {
300 let start = self.nss_range().end + RCOMP_PREFIX.len();
302 start..start + r_component_len.get() as usize
303 })
304 }
305
306 #[inline]
308 fn pre_q_component_end(&self) -> usize {
309 self.r_component_range().unwrap_or_else(|| self.nss_range()).end
310 }
311
312 #[inline]
313 fn q_component_range(&self) -> Option<Range<usize>> {
314 self.q_component_len.map(|q_component_len| {
315 let start = self.pre_q_component_end() + QCOMP_PREFIX.len();
317 start..start + q_component_len.get() as usize
318 })
319 }
320
321 #[inline]
323 fn pre_f_component_end(&self) -> usize {
324 self.q_component_range()
325 .or_else(|| self.r_component_range())
326 .unwrap_or_else(|| self.nss_range())
327 .end
328 }
329
330 #[cfg(feature = "alloc")]
332 #[inline]
333 pub(crate) fn into_owned(self) -> Urn {
334 Urn(UrnSlice {
335 urn: match self.urn {
336 TriCow::Owned(s) => TriCow::Owned(s),
337 TriCow::Borrowed(s) => TriCow::Owned(s.to_owned()),
338 TriCow::MutBorrowed(s) => TriCow::Owned(s.to_owned()),
339 },
340 nid_len: self.nid_len,
341 nss_len: self.nss_len,
342 r_component_len: self.r_component_len,
343 q_component_len: self.q_component_len,
344 })
345 }
346
347 #[inline]
348 fn f_component_start(&self) -> Option<usize> {
349 Some(self.pre_f_component_end()).filter(|x| *x < self.urn.len()).map(|x| x + FCOMP_PREFIX.len())
351 }
352
353 #[must_use]
374 #[inline]
375 pub fn as_str(&self) -> &str {
376 &self.urn
377 }
378
379 #[must_use]
383 #[inline]
384 pub fn nid(&self) -> &str {
385 &self.urn[self.nid_range()]
386 }
387 pub fn set_nid(&mut self, nid: &str) -> Result<()> {
393 let has_upper = check_nid(nid)?;
394 let nid_len = u8::try_from(nid.len()).ok().and_then(NonZeroU8::new).ok_or(Error::InvalidNid)?;
396 let range = self.nid_range();
397 let start = range.start;
398 self.urn.replace_range(range, nid)?;
399 if has_upper {
400 self.urn.make_lowercase(start..start + nid.len())?;
403 }
404 self.nid_len = nid_len;
405 Ok(())
406 }
407 #[must_use]
414 #[inline]
415 pub fn nss(&self) -> &str {
416 &self.urn[self.nss_range()]
417 }
418 pub fn set_nss(&mut self, nss: &str) -> Result<()> {
427 let (end, needs_norm) = validate_nss(nss);
428 if nss.is_empty() || end != nss.len() {
429 return Err(Error::InvalidNss);
430 }
431 let nss_len = u32::try_from(nss.len()).ok().and_then(NonZeroU32::new).ok_or(Error::InvalidNss)?;
433 let range = self.nss_range();
434 let start = range.start;
435 self.urn.replace_range(range, nss)?;
436 if needs_norm {
437 normalize_range(&mut self.urn, start..start + nss.len())?;
438 }
439 self.nss_len = nss_len;
440 Ok(())
441 }
442 #[must_use]
453 #[inline]
454 pub fn r_component(&self) -> Option<&str> {
455 self.r_component_range().map(|range| &self.urn[range])
456 }
457 pub fn set_r_component(&mut self, r_component: Option<&str>) -> Result<()> {
467 if let Some(rc) = r_component {
468 let (end, needs_norm) = validate_r_component(rc);
469 if rc.is_empty() || end != rc.len() {
470 return Err(Error::InvalidRComponent);
471 }
472 let rc_len = u32::try_from(rc.len()).ok().and_then(NonZeroU32::new).ok_or(Error::InvalidRComponent)?;
473 let range = if let Some(range) = self.r_component_range() {
474 range
475 } else {
476 let nss_end = self.nss_range().end;
478 self.urn.replace_range(nss_end..nss_end, RCOMP_PREFIX)?;
479 nss_end + RCOMP_PREFIX.len()..nss_end + RCOMP_PREFIX.len()
480 };
481 let start = range.start;
482 self.urn.replace_range(range, rc)?;
483 if needs_norm {
484 normalize_range(&mut self.urn, start..start + rc.len())?;
485 }
486 self.r_component_len = Some(rc_len);
487 } else if let Some(mut range) = self.r_component_range() {
488 range.start -= RCOMP_PREFIX.len();
489 self.urn.replace_range(range, "")?;
490 self.r_component_len = None;
491 }
492 Ok(())
493 }
494 #[must_use]
505 #[inline]
506 pub fn q_component(&self) -> Option<&str> {
507 self.q_component_range().map(|range| &self.urn[range])
508 }
509 pub fn set_q_component(&mut self, q_component: Option<&str>) -> Result<()> {
519 if let Some(qc) = q_component {
520 let (end, needs_norm) = validate_q_component(qc);
521 if qc.is_empty() || end != qc.len() {
522 return Err(Error::InvalidQComponent);
523 }
524 let qc_len = u32::try_from(qc.len()).ok().and_then(NonZeroU32::new).ok_or(Error::InvalidQComponent)?;
525 let range = if let Some(range) = self.q_component_range() {
526 range
527 } else {
528 let pre_qc_end = self.pre_q_component_end();
530 self.urn.replace_range(pre_qc_end..pre_qc_end, QCOMP_PREFIX)?;
531 pre_qc_end + QCOMP_PREFIX.len()..pre_qc_end + QCOMP_PREFIX.len()
532 };
533 let start = range.start;
534 self.urn.replace_range(range, qc)?;
535 if needs_norm {
536 normalize_range(&mut self.urn, start..start + qc.len())?;
537 }
538 self.q_component_len = Some(qc_len);
539 } else if let Some(mut range) = self.q_component_range() {
540 range.start -= QCOMP_PREFIX.len();
541 self.urn.replace_range(range, "")?;
542 self.q_component_len = None;
543 }
544 Ok(())
545 }
546 #[must_use]
556 #[inline]
557 pub fn f_component(&self) -> Option<&str> {
558 self.f_component_start().map(|start| &self.urn[start..])
559 }
560 pub fn set_f_component(&mut self, f_component: Option<&str>) -> Result<()> {
570 if let Some(fc) = f_component {
571 let (end, needs_norm) = validate_f_component(fc);
572 if end != fc.len() {
573 return Err(Error::InvalidFComponent);
574 }
575 let start = if let Some(start) = self.f_component_start() {
576 start
577 } else {
578 let range = self.urn.len()..self.urn.len();
579 self.urn.replace_range(range, FCOMP_PREFIX)?;
580 self.urn.len()
581 };
582 let len = self.urn.len();
583 self.urn.replace_range(start..len, fc)?;
584 if needs_norm {
585 normalize_range(&mut self.urn, start..start + fc.len())?;
586 }
587 } else if let Some(start) = self.f_component_start() {
588 let len = self.urn.len();
589 self.urn.replace_range(start - FCOMP_PREFIX.len()..len, "")?;
590 }
591 Ok(())
592 }
593}
594
595#[cfg(feature = "alloc")]
596#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
597impl<'a> ToOwned for UrnSlice<'a> {
598 type Owned = Urn;
599 fn to_owned(&self) -> Self::Owned {
600 Urn::from(self)
601 }
602}
603
604impl fmt::Debug for UrnSlice<'_> {
605 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
606 write!(f, "UrnSlice({})", self.as_str())
607 }
608}
609
610#[cfg(feature = "alloc")]
611impl PartialEq<Urn> for UrnSlice<'_> {
612 fn eq(&self, other: &Urn) -> bool {
613 self == &other.0
614 }
615}
616
617impl AsRef<[u8]> for UrnSlice<'_> {
618 fn as_ref(&self) -> &[u8] {
619 self.urn.as_bytes()
620 }
621}
622
623impl AsRef<str> for UrnSlice<'_> {
624 fn as_ref(&self) -> &str {
625 &self.urn
626 }
627}
628
629impl UrnSlice<'_> {
630 #[inline]
635 fn eq_slice(&self) -> &str {
636 #[cfg(feature = "exact-eq")]
637 {
638 &self.urn[..]
639 }
640 #[cfg(not(feature = "exact-eq"))]
641 {
642 &self.urn[..self.nss_range().end]
643 }
644 }
645}
646
647impl PartialEq for UrnSlice<'_> {
648 fn eq(&self, other: &Self) -> bool {
649 self.eq_slice() == other.eq_slice()
650 }
651}
652
653impl Eq for UrnSlice<'_> {}
654
655impl Ord for UrnSlice<'_> {
656 fn cmp(&self, other: &Self) -> Ordering {
657 self.eq_slice().cmp(other.eq_slice())
658 }
659}
660
661impl PartialOrd for UrnSlice<'_> {
662 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
663 Some(self.cmp(other))
664 }
665}
666
667impl Hash for UrnSlice<'_> {
668 fn hash<H: hash::Hasher>(&self, state: &mut H) {
669 self.eq_slice().hash(state);
670 }
671}
672
673impl fmt::Display for UrnSlice<'_> {
674 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
675 f.write_str(&self.urn)
676 }
677}
678
679#[cfg(feature = "alloc")]
680#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
681impl FromStr for UrnSlice<'_> {
682 type Err = Error;
683 fn from_str(s: &str) -> Result<Self> {
684 parse_urn(TriCow::Owned(s.to_owned()))
685 }
686}
687
688impl<'a> TryFrom<&'a str> for UrnSlice<'a> {
689 type Error = Error;
690 fn try_from(value: &'a str) -> Result<Self> {
691 parse_urn(TriCow::Borrowed(value))
692 }
693}
694
695impl<'a> TryFrom<&'a mut str> for UrnSlice<'a> {
709 type Error = Error;
710 fn try_from(value: &'a mut str) -> Result<Self> {
711 parse_urn(TriCow::MutBorrowed(value))
712 }
713}
714
715#[cfg(feature = "alloc")]
716#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
717impl TryFrom<String> for UrnSlice<'static> {
718 type Error = Error;
719 fn try_from(value: String) -> Result<Self> {
720 parse_urn(TriCow::Owned(value))
721 }
722}
723
724impl<'a> TryFrom<&'a [u8]> for UrnSlice<'a> {
725 type Error = Error;
726 fn try_from(value: &'a [u8]) -> Result<Self> {
727 let s = core::str::from_utf8(value).map_err(|_| Error::InvalidUtf8)?;
728 parse_urn(TriCow::Borrowed(s))
729 }
730}
731
732impl<'a> TryFrom<&'a mut [u8]> for UrnSlice<'a> {
733 type Error = Error;
734 fn try_from(value: &'a mut [u8]) -> Result<Self> {
735 let s = core::str::from_utf8_mut(value).map_err(|_| Error::InvalidUtf8)?;
736 parse_urn(TriCow::MutBorrowed(s))
737 }
738}
739
740#[cfg(feature = "alloc")]
741#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
742impl TryFrom<Vec<u8>> for UrnSlice<'static> {
743 type Error = Error;
744 fn try_from(value: Vec<u8>) -> Result<Self> {
745 let s = String::from_utf8(value).map_err(|_| Error::InvalidUtf8)?;
746 parse_urn(TriCow::Owned(s))
747 }
748}
749
750#[cfg(feature = "alloc")]
768#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
769#[derive(Debug)]
770#[must_use]
771pub struct UrnBuilder<'a> {
772 nid: &'a str,
773 nss: &'a str,
774 r_component: Option<&'a str>,
775 q_component: Option<&'a str>,
776 f_component: Option<&'a str>,
777}
778
779#[cfg(feature = "alloc")]
780impl<'a> UrnBuilder<'a> {
781 pub const fn new(nid: &'a str, nss: &'a str) -> Self {
789 Self {
790 nid,
791 nss,
792 r_component: None,
793 q_component: None,
794 f_component: None,
795 }
796 }
797 pub const fn nid(mut self, nid: &'a str) -> Self {
799 self.nid = nid;
800 self
801 }
802 pub const fn nss(mut self, nss: &'a str) -> Self {
807 self.nss = nss;
808 self
809 }
810 pub const fn r_component(mut self, r_component: Option<&'a str>) -> Self {
815 self.r_component = r_component;
816 self
817 }
818 pub const fn q_component(mut self, q_component: Option<&'a str>) -> Self {
823 self.q_component = q_component;
824 self
825 }
826 pub const fn f_component(mut self, f_component: Option<&'a str>) -> Self {
831 self.f_component = f_component;
832 self
833 }
834 pub fn build(self) -> Result<Urn> {
841 self.build_inner().map(Urn)
842 }
843
844 pub fn build_slice(self) -> Result<UrnSlice<'static>> {
852 self.build_inner()
853 }
854
855 fn build_inner(self) -> Result<UrnSlice<'static>> {
856 if !is_valid_nid(self.nid) {
857 return Err(Error::InvalidNid);
858 }
859 if self.nss.is_empty() {
860 return Err(Error::InvalidNss);
861 }
862
863 let (nss_end_in, nss_needs_norm) = validate_nss(self.nss);
866 if nss_end_in != self.nss.len() {
867 return Err(Error::InvalidNss);
868 }
869 let rc_needs_norm = if let Some(rc) = self.r_component {
870 let (end, needs_norm) = validate_r_component(rc);
871 if rc.is_empty() || end != rc.len() {
872 return Err(Error::InvalidRComponent);
873 }
874 needs_norm
875 } else {
876 false
877 };
878 let qc_needs_norm = if let Some(qc) = self.q_component {
879 let (end, needs_norm) = validate_q_component(qc);
880 if qc.is_empty() || end != qc.len() {
881 return Err(Error::InvalidQComponent);
882 }
883 needs_norm
884 } else {
885 false
886 };
887 let fc_needs_norm = if let Some(fc) = self.f_component {
888 let (end, needs_norm) = validate_f_component(fc);
889 if end != fc.len() {
890 return Err(Error::InvalidFComponent);
891 }
892 needs_norm
893 } else {
894 false
895 };
896
897 let total = URN_PREFIX.len()
898 + self.nid.len()
899 + NID_NSS_SEPARATOR.len()
900 + self.nss.len()
901 + self.r_component.map_or(0, |x| RCOMP_PREFIX.len() + x.len())
902 + self.q_component.map_or(0, |x| QCOMP_PREFIX.len() + x.len())
903 + self.f_component.map_or(0, |x| FCOMP_PREFIX.len() + x.len());
904 let mut buf = String::with_capacity(total);
905 buf.push_str(URN_PREFIX);
906 buf.push_str(self.nid);
907 buf.push_str(NID_NSS_SEPARATOR);
908 let nss_start = buf.len();
909 buf.push_str(self.nss);
910 let nss_end = buf.len();
911
912 let rc_range = self.r_component.map(|rc| {
913 buf.push_str(RCOMP_PREFIX);
914 let start = buf.len();
915 buf.push_str(rc);
916 start..buf.len()
917 });
918 let qc_range = self.q_component.map(|qc| {
919 buf.push_str(QCOMP_PREFIX);
920 let start = buf.len();
921 buf.push_str(qc);
922 start..buf.len()
923 });
924 let fc_range = self.f_component.map(|fc| {
925 buf.push_str(FCOMP_PREFIX);
926 let start = buf.len();
927 buf.push_str(fc);
928 start..buf.len()
929 });
930
931 let mut s = TriCow::Owned(buf);
932 if nss_needs_norm {
933 normalize_range(&mut s, nss_start..nss_end)?;
934 }
935 if let Some(range) = rc_range.as_ref().filter(|_| rc_needs_norm) {
936 normalize_range(&mut s, range.clone())?;
937 }
938 if let Some(range) = qc_range.as_ref().filter(|_| qc_needs_norm) {
939 normalize_range(&mut s, range.clone())?;
940 }
941 if let Some(range) = fc_range.as_ref().filter(|_| fc_needs_norm) {
942 normalize_range(&mut s, range.clone())?;
943 }
944 let nid_len = u8::try_from(self.nid.len()).ok().and_then(NonZeroU8::new).ok_or(Error::InvalidNid)?;
946 let nss_len = u32::try_from(self.nss.len()).ok().and_then(NonZeroU32::new).ok_or(Error::InvalidNss)?;
948 let r_component_len = self
949 .r_component
950 .map(|x| u32::try_from(x.len()).ok().and_then(NonZeroU32::new).ok_or(Error::InvalidRComponent))
951 .transpose()?;
952 let q_component_len = self
953 .q_component
954 .map(|x| u32::try_from(x.len()).ok().and_then(NonZeroU32::new).ok_or(Error::InvalidQComponent))
955 .transpose()?;
956 Ok(UrnSlice {
957 urn: s,
959 nid_len,
960 nss_len,
961 r_component_len,
962 q_component_len,
963 })
964 }
965}
966
967#[cfg(test)]
968#[allow(clippy::unwrap_used, clippy::panic, clippy::expect_used)]
969mod tests {
970 use super::*;
971
972 #[cfg(not(feature = "std"))]
973 use super::alloc::string::ToString;
974 #[cfg(all(not(feature = "std"), feature = "alloc"))]
975 use super::alloc::{vec, vec::Vec};
976
977 #[test]
978 fn it_works() {
979 UrnSlice::try_from("6*�").unwrap_err();
980 #[cfg(feature = "alloc")]
981 assert_eq!(
982 UrnSlice::try_from("urn:nbn:de:bvb:19-146642").unwrap(),
983 UrnBuilder::new("nbn", "de:bvb:19-146642").build().unwrap()
984 );
985 assert_eq!(UrnSlice::try_from("urn:nbn:de:bvb:19-146642").unwrap().to_string(), "urn:nbn:de:bvb:19-146642");
986
987 #[cfg(feature = "alloc")]
988 assert_eq!(
989 UrnSlice::try_from("urn:example:foo-bar-baz-qux?+CCResolve:cc=uk#test").unwrap(),
990 UrnBuilder::new("example", "foo-bar-baz-qux")
991 .r_component(Some("CCResolve:cc=uk"))
992 .f_component(Some("test"))
993 .build()
994 .unwrap()
995 );
996 assert_eq!(
997 UrnSlice::try_from("urn:example:foo-bar-baz-qux?+CCResolve:cc=uk#test")
998 .unwrap()
999 .f_component()
1000 .unwrap(),
1001 "test"
1002 );
1003 assert_eq!(
1004 UrnSlice::try_from("urn:example:foo-bar-baz-qux?+CCResolve:cc=uk#test")
1005 .unwrap()
1006 .r_component()
1007 .unwrap(),
1008 "CCResolve:cc=uk"
1009 );
1010 assert_eq!(
1011 UrnSlice::try_from("urn:example:foo-bar-baz-qux?+CCResolve:cc=uk#test").unwrap().to_string(),
1012 "urn:example:foo-bar-baz-qux?+CCResolve:cc=uk#test",
1013 );
1014
1015 #[cfg(feature = "alloc")]
1016 assert_eq!(
1017 "urn:example:weather?=op=map&lat=39.56&lon=-104.85&datetime=1969-07-21T02:56:15Z"
1018 .parse::<UrnSlice>()
1019 .unwrap(),
1020 UrnBuilder::new("example", "weather")
1021 .q_component(Some("op=map&lat=39.56&lon=-104.85&datetime=1969-07-21T02:56:15Z"))
1022 .build()
1023 .unwrap()
1024 );
1025 assert_eq!(
1026 UrnSlice::try_from("urn:example:weather?=op=map&lat=39.56&lon=-104.85&datetime=1969-07-21T02:56:15Z")
1027 .unwrap()
1028 .to_string(),
1029 "urn:example:weather?=op=map&lat=39.56&lon=-104.85&datetime=1969-07-21T02:56:15Z"
1030 );
1031
1032 #[cfg(all(feature = "alloc", not(feature = "exact-eq")))]
1035 assert_eq!(
1036 "uRn:eXaMpLe:%3d%3a?=aoiwnfuafo".parse::<UrnSlice>().unwrap(),
1037 UrnBuilder::new("example", "%3D%3a").build().unwrap()
1038 );
1039 let mut arr = *b"uRn:eXaMpLe:%3d%3a?=aoiwnfuafo";
1040 assert_eq!(
1041 UrnSlice::try_from(core::str::from_utf8_mut(&mut arr[..]).unwrap()).unwrap().as_str(),
1042 "urn:example:%3D%3A?=aoiwnfuafo",
1043 );
1044
1045 assert_eq!(UrnSlice::try_from("urn:-example:abcd"), Err(Error::InvalidNid));
1046 assert_eq!(UrnSlice::try_from("urn:example:/abcd"), Err(Error::InvalidNss));
1047 assert_eq!(UrnSlice::try_from("urn:a:abcd"), Err(Error::InvalidNid));
1048 assert_eq!(UrnSlice::try_from("urn:0123456789abcdef0123456789abcdef0:abcd"), Err(Error::InvalidNid));
1049 let _ = UrnSlice::try_from("urn:0123456789abcdef0123456789abcdef:abcd").unwrap();
1050 assert_eq!(UrnSlice::try_from("urn:example"), Err(Error::InvalidNss));
1051 assert_eq!(UrnSlice::try_from("urn:example:"), Err(Error::InvalidNss));
1052 assert_eq!(UrnSlice::try_from("urn:example:%"), Err(Error::InvalidNss));
1053 assert_eq!(UrnSlice::try_from("urn:example:%a"), Err(Error::InvalidNss));
1054 assert_eq!(UrnSlice::try_from("urn:example:%a_"), Err(Error::InvalidNss));
1055 let mut arr = *b"urn:example:%a0?+";
1056 assert_eq!(
1057 UrnSlice::try_from(core::str::from_utf8_mut(&mut arr[..]).unwrap()),
1058 Err(Error::InvalidRComponent)
1059 );
1060 let mut arr = *b"urn:example:%a0?+%a0?=";
1061 assert_eq!(
1062 UrnSlice::try_from(core::str::from_utf8_mut(&mut arr[..]).unwrap()),
1063 Err(Error::InvalidQComponent)
1064 );
1065 let mut arr = *b"urn:example:%a0?+%a0?=a";
1066 assert_eq!(
1067 UrnSlice::try_from(core::str::from_utf8_mut(&mut arr[..]).unwrap())
1068 .unwrap()
1069 .r_component()
1070 .unwrap(),
1071 "%A0",
1072 );
1073
1074 #[cfg(feature = "alloc")]
1075 {
1076 let mut urn = "urn:example:test".parse::<UrnSlice>().unwrap();
1077 urn.set_f_component(Some("f-component")).unwrap();
1078 assert_eq!(urn.f_component(), Some("f-component"));
1079 assert_eq!(urn.as_str(), "urn:example:test#f-component");
1080 urn.set_f_component(Some("")).unwrap();
1081 assert_eq!(urn.f_component(), Some(""));
1082 assert_eq!(urn.as_str(), "urn:example:test#");
1083 urn.set_q_component(Some("abcd")).unwrap();
1084 assert_eq!(urn.q_component(), Some("abcd"));
1085 assert_eq!(urn.as_str(), "urn:example:test?=abcd#");
1086 assert!(urn.set_q_component(Some("")).is_err());
1087 urn.set_r_component(Some("%2a")).unwrap();
1088 assert_eq!(urn.r_component(), Some("%2A"));
1089 assert_eq!(urn.as_str(), "urn:example:test?+%2A?=abcd#");
1090 urn.set_nid("a-b").unwrap();
1091 assert_eq!(urn.as_str(), "urn:a-b:test?+%2A?=abcd#");
1092 urn.set_r_component(None).unwrap();
1093 assert_eq!(urn.as_str(), "urn:a-b:test?=abcd#");
1094 assert_eq!(urn.r_component(), None);
1095 }
1096 }
1097
1098 #[test]
1101 fn mut_str_normalizes_in_place() {
1102 let mut buf = *b"uRn:eXaMpLe:Foo-Bar";
1103 let buf_ptr = buf.as_ptr();
1104 let s = core::str::from_utf8_mut(&mut buf[..]).unwrap();
1105 let urn = UrnSlice::try_from(s).unwrap();
1106 assert_eq!(urn.as_str().as_ptr(), buf_ptr);
1107 assert_eq!(urn.as_str(), "urn:example:Foo-Bar");
1108 }
1109
1110 #[cfg(all(feature = "alloc", not(feature = "exact-eq")))]
1111 #[test]
1112 fn ord_matches_eq() {
1113 use core::cmp::Ordering;
1114 let a = UrnSlice::try_from("urn:example:abc").unwrap();
1115 let b = UrnSlice::try_from("urn:example:abd").unwrap();
1116 let c = UrnSlice::try_from("urn:example:abc?=q").unwrap(); assert_eq!(a.cmp(&b), Ordering::Less);
1118 assert_eq!(b.cmp(&a), Ordering::Greater);
1119 assert_eq!(a.cmp(&c), Ordering::Equal);
1120 assert_eq!(a, c);
1121 let ao = Urn::try_from("urn:example:abc").unwrap();
1123 let bo = Urn::try_from("urn:example:abd").unwrap();
1124 assert!(ao < bo);
1125 }
1126
1127 #[cfg(all(feature = "alloc", feature = "exact-eq"))]
1128 #[test]
1129 fn exact_eq_includes_rqf() {
1130 use core::cmp::Ordering;
1131 #[cfg(feature = "std")]
1132 use core::hash::BuildHasher;
1133 #[cfg(feature = "std")]
1134 use std::collections::hash_map::RandomState;
1135
1136 let a = UrnSlice::try_from("urn:example:abc").unwrap();
1137 let b = UrnSlice::try_from("urn:example:abc?=q").unwrap();
1138 let c = UrnSlice::try_from("urn:example:abc?=q").unwrap();
1139 let d = UrnSlice::try_from("urn:example:abc#frag").unwrap();
1140 let e = UrnSlice::try_from("urn:example:abc?+r").unwrap();
1141
1142 assert_ne!(a, b);
1144 assert_ne!(a, d);
1145 assert_ne!(a, e);
1146 assert_ne!(b, d);
1147 assert_eq!(b, c);
1148 assert_eq!(a.cmp(&b), Ordering::Less);
1149
1150 #[cfg(feature = "std")]
1152 {
1153 let rs = RandomState::new();
1154 assert_eq!(rs.hash_one(&b), rs.hash_one(&c));
1155 assert_ne!(rs.hash_one(&a), rs.hash_one(&b));
1156 }
1157 }
1158
1159 #[cfg(feature = "alloc")]
1160 #[test]
1161 fn decode_iter_matches_decode() {
1162 use crate::percent::{decode_nss, decode_nss_iter};
1163 let input = "hello%20world%21";
1164 let via_iter: Result<Vec<u8>, _> = decode_nss_iter(input).collect();
1165 assert_eq!(via_iter.unwrap(), decode_nss(input).unwrap().into_bytes());
1166
1167 let bad = "%zz";
1169 let res: Result<Vec<u8>, _> = decode_nss_iter(bad).collect();
1170 assert_eq!(res, Err(Error::InvalidNss));
1171 }
1172
1173 #[test]
1174 fn byte_slice_try_from_roundtrip() {
1175 let bytes: &[u8] = b"urn:example:foo";
1176 let urn = UrnSlice::try_from(bytes).unwrap();
1177 assert_eq!(urn.as_str(), "urn:example:foo");
1178
1179 let bad: &[u8] = &[0xFFu8, b'u', b'r', b'n'];
1181 assert_eq!(UrnSlice::try_from(bad).unwrap_err(), Error::InvalidUtf8);
1182 }
1183
1184 #[test]
1185 fn mut_byte_slice_normalizes_in_place() {
1186 let mut buf = *b"uRn:eXaMpLe:Foo-Bar";
1187 let buf_ptr = buf.as_ptr();
1188 let urn = UrnSlice::try_from(&mut buf[..]).unwrap();
1189 assert_eq!(urn.as_str().as_ptr(), buf_ptr);
1190 assert_eq!(urn.as_str(), "urn:example:Foo-Bar");
1191 }
1192
1193 #[cfg(feature = "alloc")]
1194 #[test]
1195 fn vec_try_from_roundtrip() {
1196 let v: Vec<u8> = b"urn:example:foo".to_vec();
1197 let urn = UrnSlice::try_from(v).unwrap();
1198 assert_eq!(urn.as_str(), "urn:example:foo");
1199
1200 let bad: Vec<u8> = vec![0xFFu8];
1201 assert_eq!(UrnSlice::try_from(bad).unwrap_err(), Error::InvalidUtf8);
1202 }
1203
1204 #[cfg(feature = "alloc")]
1205 #[test]
1206 fn build_slice_matches_build() {
1207 let a = UrnBuilder::new("example", "1234:5678").build_slice().unwrap();
1208 let b = UrnBuilder::new("example", "1234:5678").build().unwrap();
1209 assert_eq!(a.as_str(), b.as_str());
1210 assert_eq!(a.as_str(), "urn:example:1234:5678");
1211 }
1212
1213 #[test]
1214 fn from_static_normalized_borrows() {
1215 let s: &'static str = "urn:example:foo";
1217 let urn = UrnSlice::from_static(s).unwrap();
1218 assert_eq!(urn.as_str().as_ptr(), s.as_ptr());
1220 }
1221
1222 #[cfg(feature = "alloc")]
1223 #[test]
1224 fn from_static_denormalized_still_ok() {
1225 let urn = UrnSlice::from_static("uRn:eXaMpLe:%3d%3a").unwrap();
1227 assert_eq!(urn.as_str(), "urn:example:%3D%3A");
1228 }
1229
1230 #[cfg(feature = "alloc")]
1231 #[test]
1232 fn encode_iter_matches_encode() {
1233 use crate::percent::{
1234 encode_f_component,
1235 encode_f_component_iter,
1236 encode_nss,
1237 encode_nss_iter,
1238 encode_q_component,
1239 encode_q_component_iter,
1240 encode_r_component,
1241 encode_r_component_iter,
1242 };
1243 let input = "hello world!😂";
1244
1245 let via_iter: Vec<u8> = encode_nss_iter(input).collect();
1246 assert_eq!(via_iter, encode_nss(input).unwrap().into_bytes());
1247
1248 let via_iter: Vec<u8> = encode_r_component_iter(input).collect();
1249 assert_eq!(via_iter, encode_r_component(input).unwrap().into_bytes());
1250
1251 let via_iter: Vec<u8> = encode_q_component_iter(input).collect();
1252 assert_eq!(via_iter, encode_q_component(input).unwrap().into_bytes());
1253
1254 let via_iter: Vec<u8> = encode_f_component_iter(input).collect();
1255 assert_eq!(via_iter, encode_f_component(input).unwrap().into_bytes());
1256
1257 let r = encode_r_component_iter("?x").collect::<Vec<u8>>();
1260 assert_eq!(r, encode_r_component("?x").unwrap().into_bytes());
1261 let r = encode_r_component_iter("x?y").collect::<Vec<u8>>();
1262 assert_eq!(r, encode_r_component("x?y").unwrap().into_bytes());
1263 }
1264}