1use core::borrow::Borrow;
2use core::cmp::{Ordering, PartialOrd};
3use core::convert::{TryFrom, TryInto};
4use core::fmt;
5use core::ops::{
6 Deref, Index, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive,
7};
8
9#[cfg(feature = "std")]
10use std::{
11 borrow::{Cow, ToOwned},
12 iter::FromIterator,
13 ops::{Add, AddAssign, DerefMut},
14};
15
16#[cfg(feature = "compact_str")]
22pub type CompactString = compact_str::CompactString;
23
24#[cfg(all(not(feature = "compact_str"), feature = "std"))]
30pub type CompactString = String;
31
32use crate::selectors;
33use crate::selectors::CharSelector;
34use crate::Error;
35use crate::{validate_name, validate_ncname};
36
37macro_rules! rxml_unsafe_str_construct_doc {
38 ($name:ident, $other:ident) => {
39 concat!(
40 "Construct a `",
41 stringify!($name),
42 "` without enforcing anything\n",
43 "\n",
44 "# Safety\n",
45 "\n",
46 "The caller is responsible for ensuring that the passed [`",
47 stringify!($other),
48 "`] is in fact a valid `",
49 stringify!($name),
50 "`.\n",
51 )
52 };
53}
54
55macro_rules! rxml_safe_str_construct_doc {
56 ($name:ident, $other:ident, $more:expr) => {
57 concat!(
58 "Converts a [`",
59 stringify!($other),
60 "`] to a `",
61 stringify!($name),
62 "`.\n",
63 "\n",
64 "If the given `",
65 stringify!($other),
66 "` does not conform to the restrictions imposed by `",
67 stringify!($name),
68 "`, an error is returned.\n",
69 $more
70 )
71 };
72}
73
74macro_rules! rxml_split_at_example {
75 ($borrowed:ty) => {
76 concat!(
77 "\n\n```\n",
78 "# use std::convert::TryInto;\n",
79 "# use rxml_validation::",
80 stringify!($borrowed),
81 ";\n",
82 "let value: &",
83 stringify!($borrowed),
84 " = \"foobar\".try_into().unwrap();\n",
85 "let (lhs, rhs) = value.split_at(3);\n",
86 "assert_eq!(lhs, \"foo\");\n",
87 "assert_eq!(rhs, \"bar\");\n",
88 "```\n",
89 )
90 };
91}
92
93#[cfg(feature = "std")]
94macro_rules! rxml_make_ascii_lowercase_example {
95 ($owned:ty, $borrowed:ty) => {
96 concat!(
97 "\n\n# Example\n\n```\n",
98 "# use std::convert::TryInto;\n",
99 "# use rxml_validation::{",
100 stringify!($borrowed),
101 ", ",
102 stringify!($owned),
103 "};\n",
104 "let mut owned: ",
105 stringify!($owned),
106 " = \"FÖöBar\".try_into().unwrap();\n",
107 "let borrowed: &mut ",
108 stringify!($borrowed),
109 " = &mut owned;\n",
110 "borrowed.make_ascii_lowercase();\n",
111 "assert_eq!(borrowed, \"fÖöbar\");\n",
112 "```\n",
113 )
114 };
115}
116
117#[cfg(not(feature = "std"))]
118macro_rules! rxml_make_ascii_lowercase_example {
119 ($owned:ty, $borrowed:ty) => {
120 ""
121 };
122}
123
124#[cfg(feature = "std")]
125macro_rules! rxml_make_ascii_uppercase_example {
126 ($owned:ty, $borrowed:ty) => {
127 concat!(
128 "\n\n# Example\n\n```\n",
129 "# use std::convert::TryInto;\n",
130 "# use rxml_validation::{",
131 stringify!($borrowed),
132 ", ",
133 stringify!($owned),
134 "};\n",
135 "let mut owned: ",
136 stringify!($owned),
137 " = \"FÖöBar\".try_into().unwrap();\n",
138 "let borrowed: &mut ",
139 stringify!($borrowed),
140 " = &mut owned;\n",
141 "borrowed.make_ascii_uppercase();\n",
142 "assert_eq!(borrowed, \"FÖöBAR\");\n",
143 "```\n",
144 )
145 };
146}
147
148#[cfg(not(feature = "std"))]
149macro_rules! rxml_make_ascii_uppercase_example {
150 ($owned:ty, $borrowed:ty) => {
151 ""
152 };
153}
154
155#[cfg(feature = "std")]
156macro_rules! rxml_split_off_panic_on_empty {
157 () => {
158 concat!(
159 "\n",
160 "# Panics\n",
161 "\n",
162 "If `idx` is 0 or equal to the length minus one, as the empty ",
163 "string is not valid.\n",
164 )
165 };
166}
167
168#[cfg(feature = "std")]
169macro_rules! rxml_split_off_panics {
170 (NcName) => {
171 rxml_split_off_panic_on_empty!()
172 };
173 (Name) => {
174 rxml_split_off_panic_on_empty!()
175 };
176}
177
178#[cfg(feature = "std")]
179macro_rules! rxml_split_off_example {
180 ($ty:ident) => {
181 concat!(
182 "\n",
183 "```\n",
184 "# use std::convert::TryInto;\n",
185 "# use rxml_validation::",
186 stringify!($ty),
187 ";\n",
188 "let mut value: ",
189 stringify!($ty),
190 " = \"foobar\".try_into().unwrap();\n",
191 "let rhs: ",
192 stringify!($ty),
193 " = value.split_off(3);\n",
194 "assert_eq!(value, \"foo\");\n",
195 "assert_eq!(rhs, \"bar\");\n",
196 "```\n",
197 )
198 };
199}
200
201#[cfg(feature = "std")]
202macro_rules! rxml_insert_str_example {
203 ($owned:ident, $borrowed:ident) => {
204 concat!(
205 "\n",
206 "```\n",
207 "# use std::convert::TryInto;\n",
208 "# use rxml_validation::{",
209 stringify!($owned),
210 ", ",
211 stringify!($borrowed),
212 "};\n",
213 "let mut value: ",
214 stringify!($owned),
215 " = \"foobaz\".try_into().unwrap();\n",
216 "let to_insert: &",
217 stringify!($borrowed),
218 " = \"bar\".try_into().unwrap();\n",
219 "value.insert_str(3, to_insert);\n",
220 "assert_eq!(value, \"foobarbaz\");\n",
221 "```\n",
222 )
223 };
224}
225
226#[cfg(feature = "std")]
227macro_rules! rxml_push_str_example {
228 ($owned:ident, $borrowed:ident) => {
229 concat!(
230 "\n",
231 "```\n",
232 "# use std::convert::TryInto;\n",
233 "# use rxml_validation::{",
234 stringify!($owned),
235 ", ",
236 stringify!($borrowed),
237 "};\n",
238 "let mut value: ",
239 stringify!($owned),
240 " = \"foobar\".try_into().unwrap();\n",
241 "let to_append: &",
242 stringify!($borrowed),
243 " = \"baz\".try_into().unwrap();\n",
244 "value.push_str(to_append);\n",
245 "assert_eq!(value, \"foobarbaz\");\n",
246 "```\n",
247 )
248 };
249}
250
251#[cfg(all(not(feature = "compact_str"), feature = "std"))]
252macro_rules! rxml_non_compact_str_only_note {
253 (CompactString) => {
254 "\n# Note\nThis function is only available *without* the `compact_str` feature!\n"
255 };
256 ($other:ident) => {
257 ""
258 };
259}
260
261#[cfg(feature = "std")]
262macro_rules! rxml_custom_string_type {
263 (
264 $(#[$outer:meta])*
265 pub struct $name:ident($string:ident) use $check:ident => $borrowed:ident;
266 ) => {
267 $(#[$outer])*
268 #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord)]
269 #[repr(transparent)]
270 pub struct $name($string);
271
272 impl $name {
273 pub fn into_inner(self) -> $string {
275 self.0
276 }
277
278 pub fn as_str(&self) -> &str {
280 self.0.as_str()
281 }
282
283 pub fn capacity(&self) -> usize {
285 self.0.capacity()
286 }
287
288 #[doc = rxml_insert_str_example!($name, $borrowed)]
301 pub fn insert_str(&mut self, idx: usize, string: &$borrowed) {
302 self.0.insert_str(idx, &string.0);
306 }
307
308 pub fn len(&self) -> usize {
310 self.0.len()
311 }
312
313 pub fn reserve(&mut self, additional: usize) {
316 self.0.reserve(additional)
317 }
318
319 #[doc = rxml_non_compact_str_only_note!($string)]
325 #[cfg(not(feature = "compact_str"))]
326 pub fn reserve_exact(&mut self, additional: usize) {
327 self.0.reserve_exact(additional)
328 }
329
330 pub fn shrink_to(&mut self, min_capacity: usize) {
332 self.0.shrink_to(min_capacity)
333 }
334
335 pub fn shrink_to_fit(&mut self) {
337 self.0.shrink_to_fit()
338 }
339
340 #[doc = rxml_unsafe_str_construct_doc!($name, str)]
341 pub unsafe fn from_str_unchecked<T: AsRef<str>>(s: T) -> Self {
342 Self(s.as_ref().into())
343 }
344
345 #[doc = rxml_unsafe_str_construct_doc!($name, String)]
346 pub unsafe fn from_string_unchecked<T: Into<String>>(s: T) -> Self {
347 Self(s.into().into())
348 }
349
350 #[cfg(feature = "compact_str")]
351 #[allow(dead_code)]
352 unsafe fn from_auto_unchecked(s: CompactString) -> Self {
353 Self(s.into())
354 }
355
356 #[cfg(not(feature = "compact_str"))]
357 #[allow(dead_code)]
358 unsafe fn from_auto_unchecked(s: String) -> Self {
359 Self(s.into())
360 }
361
362 #[doc = rxml_unsafe_str_construct_doc!($name, CompactString)]
363 #[cfg(feature = "compact_str")]
364 #[cfg_attr(docsrs, doc(cfg(feature = "compact_str")))]
365 pub unsafe fn from_compact_str_unchecked<T: Into<CompactString>>(s: T) -> Self {
366 Self(s.into().into())
367 }
368
369 unsafe fn from_native_unchecked(s: $string) -> Self {
370 Self(s)
371 }
372
373 #[doc = rxml_push_str_example!($name, $borrowed)]
381 pub fn push_str(&mut self, v: &$borrowed) {
385 self.0.push_str(&v.0)
386 }
387 }
388
389 impl Deref for $name {
390 type Target = $borrowed;
391
392 fn deref(&self) -> &Self::Target {
393 unsafe { $borrowed::from_str_unchecked(&self.0) }
396 }
397 }
398
399 impl DerefMut for $name {
400 fn deref_mut(&mut self) -> &mut Self::Target {
401 unsafe { $borrowed::from_str_unchecked_mut(&mut self.0) }
404 }
405 }
406
407 impl Borrow<$string> for $name {
408 fn borrow(&self) -> &$string {
409 &self.0
410 }
411 }
412
413 impl Borrow<$borrowed> for $name {
414 fn borrow(&self) -> &$borrowed {
415 unsafe { $borrowed::from_str_unchecked(&self.0) }
418 }
419 }
420
421 impl Borrow<str> for $name {
422 fn borrow(&self) -> &str {
423 &self.0
424 }
425 }
426
427 impl AsRef<$string> for $name {
428 fn as_ref(&self) -> &$string {
429 &self.0
430 }
431 }
432
433 impl AsRef<$borrowed> for $name {
434 fn as_ref(&self) -> &$borrowed {
435 unsafe { $borrowed::from_str_unchecked(&self.0) }
438 }
439 }
440
441 impl AsRef<str> for $name {
442 fn as_ref(&self) -> &str {
443 &self.0
444 }
445 }
446
447 impl PartialEq<str> for $name {
448 fn eq(&self, other: &str) -> bool {
449 self.0 == other
450 }
451 }
452
453 impl PartialEq<$name> for str {
456 fn eq(&self, other: &$name) -> bool {
457 other.0 == self
458 }
459 }
460
461 impl PartialEq<&str> for $name {
462 fn eq(&self, other: &&str) -> bool {
463 &self.0 == other
464 }
465 }
466
467 impl PartialEq<$name> for &str {
468 fn eq(&self, other: &$name) -> bool {
469 other.0 == *self
470 }
471 }
472
473 impl PartialEq<$borrowed> for $name {
474 fn eq(&self, other: &$borrowed) -> bool {
475 self.0 == &other.0
476 }
477 }
478
479 impl PartialEq<$name> for $borrowed {
480 fn eq(&self, other: &$name) -> bool {
481 other.0 == &self.0
482 }
483 }
484
485 impl PartialEq<&$borrowed> for $name {
486 fn eq(&self, other: &&$borrowed) -> bool {
487 self.0 == &other.0
488 }
489 }
490
491 impl PartialEq<$name> for &$borrowed {
492 fn eq(&self, other: &$name) -> bool {
493 other.0 == &self.0
494 }
495 }
496
497 impl PartialOrd<$name> for $name {
498 fn partial_cmp(&self, other: &$name) -> Option<Ordering> {
499 self.0.partial_cmp(&other.0)
500 }
501 }
502
503 impl From<$name> for String {
504 fn from(other: $name) -> Self {
505 other.0.into()
506 }
507 }
508
509 #[cfg(feature = "compact_str")]
510 #[cfg_attr(docsrs, doc(cfg(feature = "compact_str")))]
511 impl From<$name> for CompactString {
512 fn from(other: $name) -> Self {
513 other.0.into()
514 }
515 }
516
517 impl<'x> From<$name> for Cow<'x, $borrowed> {
518 fn from(other: $name) -> Self {
519 Self::Owned(other)
520 }
521 }
522
523 impl<'x> From<Cow<'x, $borrowed>> for $name {
524 fn from(other: Cow<'x, $borrowed>) -> Self {
525 other.into_owned()
526 }
527 }
528
529 #[cfg(feature = "compact_str")]
530 #[cfg_attr(docsrs, doc(cfg(feature = "compact_str")))]
531 impl TryFrom<CompactString> for $name {
532 type Error = Error;
533
534 #[doc = rxml_safe_str_construct_doc!($name, CompactString, "")]
535 fn try_from(other: CompactString) -> Result<Self, Self::Error> {
536 $check(&other)?;
537 Ok($name(other.into()))
538 }
539 }
540
541 impl TryFrom<String> for $name {
542 type Error = Error;
543
544 #[doc = rxml_safe_str_construct_doc!($name, String, "")]
545 fn try_from(other: String) -> Result<Self, Self::Error> {
546 $check(&other)?;
547 Ok($name(other.into()))
548 }
549 }
550
551 impl TryFrom<&str> for $name {
552 type Error = Error;
553
554 #[doc = rxml_safe_str_construct_doc!($name, str, "")]
555 fn try_from(other: &str) -> Result<Self, Self::Error> {
556 $check(other)?;
557 Ok($name(other.into()))
558 }
559 }
560
561 impl fmt::Display for $name {
562 fn fmt<'f>(&self, f: &'f mut fmt::Formatter) -> fmt::Result {
563 f.write_str(&self.0 as &str)
564 }
565 }
566
567 impl Add<&$borrowed> for $name {
579 type Output = $name;
580
581 fn add(mut self, rhs: &$borrowed) -> Self::Output {
582 self += rhs;
583 self
584 }
585 }
586
587 impl AddAssign<&$borrowed> for $name {
588 fn add_assign(&mut self, rhs: &$borrowed) {
589 self.0.push_str(&rhs.0)
590 }
591 }
592
593 impl<'a> Extend<&'a $borrowed> for $name {
594 fn extend<I: IntoIterator<Item = &'a $borrowed>>(&mut self, iter: I) {
595 self.0.extend(iter.into_iter().map(|x| &x.0))
596 }
597 }
598
599 impl Extend<Box<$borrowed>> for $name {
600 fn extend<I: IntoIterator<Item = Box<$borrowed>>>(&mut self, iter: I) {
601 for item in iter {
602 self.add_assign(&item);
603 }
604 }
605 }
606
607 impl<'a> Extend<Cow<'a, $borrowed>> for $name {
608 fn extend<I: IntoIterator<Item = Cow<'a, $borrowed>>>(&mut self, iter: I) {
609 for item in iter {
610 self.add_assign(&item);
611 }
612 }
613 }
614
615 impl Extend<$name> for $name {
616 fn extend<I: IntoIterator<Item = $name>>(&mut self, iter: I) {
617 self.0.extend(iter.into_iter().map(|x| x.0))
618 }
619 }
620
621 impl<'x> FromIterator<&'x $borrowed> for $name {
622 fn from_iter<I: IntoIterator<Item = &'x $borrowed>>(iter: I) -> Self {
623 unsafe {
625 Self::from_native_unchecked(
626 <$string>::from_iter(iter.into_iter().map(|x| &x.0))
627 )
628 }
629 }
630 }
631
632 impl FromIterator<Box<$borrowed>> for $name {
633 fn from_iter<I: IntoIterator<Item = Box<$borrowed>>>(iter: I) -> Self {
634 let mut buf = <$string>::with_capacity(0);
635 for item in iter {
636 buf.push_str(&item.0);
637 }
638 unsafe {
639 Self::from_native_unchecked(buf)
640 }
641 }
642 }
643
644 impl<'x> FromIterator<Cow<'x, $borrowed>> for $name {
645 fn from_iter<I: IntoIterator<Item = Cow<'x, $borrowed>>>(iter: I) -> Self {
646 let mut buf = <$string>::with_capacity(0);
647 for item in iter {
648 buf.push_str(&item.0);
649 }
650 unsafe {
651 Self::from_native_unchecked(buf)
652 }
653 }
654 }
655 }
656}
657
658macro_rules! rxml_custom_str_type {
659 (
660 $(#[$outer:meta])*
661 pub struct $name:ident(str) use $check:ident => $owned:ident;
662 ) => {
663 $(#[$outer])*
664 #[derive(Debug, Hash, PartialEq, Eq, Ord)]
665 #[repr(transparent)]
666 pub struct $name(str);
667
668 impl $name {
669 #[doc = rxml_safe_str_construct_doc!($name, str, "")]
670 pub fn from_str<'x>(s: &'x str) -> Result<&'x Self, Error> {
671 s.try_into()
672 }
673
674 pub const fn as_str(&self) -> &str {
678 &self.0
679 }
680
681 #[doc = rxml_unsafe_str_construct_doc!($name, str)]
682 pub const unsafe fn from_str_unchecked<'x>(s: &'x str) -> &'x Self {
683 core::mem::transmute(s)
684 }
685
686 #[doc = rxml_unsafe_str_construct_doc!($name, str)]
687 pub unsafe fn from_str_unchecked_mut<'x>(s: &'x mut str) -> &'x mut Self {
688 core::mem::transmute(s)
689 }
690
691 #[doc = rxml_make_ascii_lowercase_example!($owned, $name)]
695 pub fn make_ascii_lowercase(&mut self) {
696 self.0.make_ascii_lowercase()
697 }
698
699 #[doc = rxml_make_ascii_uppercase_example!($owned, $name)]
703 pub fn make_ascii_uppercase(&mut self) {
704 self.0.make_ascii_uppercase()
705 }
706 }
707
708 impl Deref for $name {
709 type Target = str;
710
711 fn deref(&self) -> &Self::Target {
712 &self.0
713 }
714 }
715
716 impl AsRef<str> for $name {
717 fn as_ref(&self) -> &str {
718 &self.0
719 }
720 }
721
722 impl AsRef<$name> for &$name {
723 fn as_ref(&self) -> &$name {
724 &self
725 }
726 }
727
728 impl PartialEq<str> for $name {
729 fn eq(&self, other: &str) -> bool {
730 &self.0 == other
731 }
732 }
733
734 impl PartialEq<$name> for str {
735 fn eq(&self, other: &$name) -> bool {
736 self == &other.0
737 }
738 }
739
740 impl PartialOrd<$name> for $name {
741 fn partial_cmp(&self, other: &$name) -> Option<Ordering> {
742 self.0.partial_cmp(&other.0)
743 }
744 }
745
746 #[cfg(feature = "std")]
747 #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
748 impl ToOwned for $name {
749 type Owned = $owned;
750
751 fn to_owned(&self) ->Self::Owned {
752 self.into()
753 }
754 }
755
756 #[cfg(feature = "std")]
757 #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
758 impl From<&$name> for $owned {
759 fn from(other: &$name) -> Self {
760 unsafe { $owned::from_str_unchecked(&other.0) }
763 }
764 }
765
766 impl<'x> TryFrom<&'x str> for &'x $name {
767 type Error = Error;
768
769 fn try_from(other: &'x str) -> Result<Self, Self::Error> {
770 $check(other)?;
771 Ok(unsafe { core::mem::transmute(other) } )
774 }
775 }
776
777 impl fmt::Display for $name {
778 fn fmt<'f>(&self, f: &'f mut fmt::Formatter) -> fmt::Result {
779 f.write_str(&self.0)
780 }
781 }
782 }
783}
784
785macro_rules! rxml_index_impl {
786 ($ty:ty, $selcode:expr, $borrowed:ty, $rangety:ty) => {
787 impl Index<$rangety> for $ty {
788 type Output = $borrowed;
789
790 fn index(&self, index: $rangety) -> &$borrowed {
791 let tmp = &self.0[index];
792 let firstchar = tmp.chars().next();
793 if !($selcode(firstchar)) {
794 panic!(concat!("slice is not a valid ", stringify!($borrowed)));
795 }
796 unsafe { <$borrowed>::from_str_unchecked(tmp) }
798 }
799 }
800 };
801}
802
803macro_rules! rxml_splitting_impls {
804 ($ty:ident => $firstsel:path => $borrowed:ident) => {
805 rxml_splitting_impls!($ty => (|firstchar: Option<char>| firstchar.map(|x| $firstsel.select(x)).unwrap_or(false)) => $borrowed);
806 };
807 ($ty:ident => $selcode:expr => $borrowed:ident) => {
808 #[cfg(feature = "std")]
809 #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
810 rxml_index_impl!($ty, $selcode, $borrowed, Range<usize>);
811
812 #[cfg(feature = "std")]
813 #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
814 rxml_index_impl!($ty, $selcode, $borrowed, RangeFrom<usize>);
815
816 #[cfg(feature = "std")]
817 #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
818 rxml_index_impl!($ty, $selcode, $borrowed, RangeFull);
819
820 #[cfg(feature = "std")]
821 #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
822 rxml_index_impl!($ty, $selcode, $borrowed, RangeInclusive<usize>);
823
824 #[cfg(feature = "std")]
825 #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
826 rxml_index_impl!($ty, $selcode, $borrowed, RangeTo<usize>);
827
828 #[cfg(feature = "std")]
829 #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
830 rxml_index_impl!($ty, $selcode, $borrowed, RangeToInclusive<usize>);
831
832 rxml_index_impl!($borrowed, $selcode, $borrowed, Range<usize>);
833 rxml_index_impl!($borrowed, $selcode, $borrowed, RangeFrom<usize>);
834 rxml_index_impl!($borrowed, $selcode, $borrowed, RangeFull);
835 rxml_index_impl!($borrowed, $selcode, $borrowed, RangeInclusive<usize>);
836 rxml_index_impl!($borrowed, $selcode, $borrowed, RangeTo<usize>);
837 rxml_index_impl!($borrowed, $selcode, $borrowed, RangeToInclusive<usize>);
838
839
840 #[cfg(feature = "std")]
841 #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
842 impl $ty {
843 #[doc = rxml_split_off_panics!($ty)]
845 #[doc = rxml_split_off_example!($ty)]
849 pub fn split_off(&mut self, at: usize) -> Self {
850 let other = self.0.split_off(at);
851 if !<$borrowed>::verify(&other) || !<$borrowed>::verify(&self.0) {
852 panic!(concat!("split string is not a valid ", stringify!($ty)));
853 }
854 unsafe {
856 <$ty>::from_str_unchecked(other)
857 }
858 }
859 }
860
861 impl $borrowed {
862 fn verify(s: &str) -> bool {
863 let firstchar = s.chars().next();
864 return $selcode(firstchar);
865 }
866
867 #[doc = rxml_split_at_example!($borrowed)]
877 pub fn split_at(&self, mid: usize) -> (&Self, &Self) {
878 let (a, b) = self.0.split_at(mid);
879 if !Self::verify(a) || !Self::verify(b) {
880 panic!(concat!("split_at result is not a valid ", stringify!($borrowed)));
881 }
882 unsafe {
884 (
885 Self::from_str_unchecked(a),
886 Self::from_str_unchecked(b),
887 )
888 }
889 }
890
891 pub fn split_at_mut(&mut self, mid: usize) -> (&mut Self, &mut Self) {
899 let (a, b) = self.0.split_at_mut(mid);
900 if !Self::verify(a) || !Self::verify(b) {
901 panic!(concat!("split_at_mut result is not a valid ", stringify!($borrowed)));
902 }
903 unsafe {
905 (
906 Self::from_str_unchecked_mut(a),
907 Self::from_str_unchecked_mut(b),
908 )
909 }
910 }
911 }
912 }
913}
914
915macro_rules! rxml_custom_string_type_pair {
916 (
917 $(#[$ownedmeta:meta])*
918 pub struct $owned:ident($string:ident) use $check:ident;
919
920 $(#[$borrowedmeta:meta])*
921 pub struct $borrowed:ident(str);
922 ) => {
923 #[cfg(feature = "std")]
924 rxml_custom_string_type!{
925 $(#[$ownedmeta])*
926 #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
927 pub struct $owned($string) use $check => $borrowed;
928 }
929
930 rxml_custom_str_type!{
931 $(#[$borrowedmeta])*
932 pub struct $borrowed(str) use $check => $owned;
933 }
934 }
935}
936
937rxml_custom_string_type_pair! {
938 pub struct Name(CompactString) use validate_name;
968
969 pub struct NameStr(str);
982}
983
984rxml_splitting_impls! {
985 Name => selectors::CLASS_XML_NAMESTART => NameStr
986}
987
988#[cfg(feature = "std")]
989#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
990impl Name {
991 pub fn split_name(self) -> Result<(Option<NcName>, NcName), Error> {
1023 let mut name = self.0;
1024 let colon_pos = match name.find(':') {
1025 None => return Ok((None, unsafe { NcName::from_auto_unchecked(name) })),
1026 Some(pos) => pos,
1027 };
1028 if colon_pos == 0 || colon_pos == name.len() - 1 {
1029 return Err(Error::EmptyNamePart);
1030 }
1031
1032 let localname = name.split_off(colon_pos + 1);
1033 let mut prefix = name;
1034
1035 if localname.find(':').is_some() {
1036 return Err(Error::MultiColonName);
1038 };
1039 if !selectors::CLASS_XML_NAMESTART.select(localname.chars().next().unwrap()) {
1040 return Err(Error::InvalidLocalName);
1042 }
1043
1044 prefix.pop();
1045 debug_assert!(prefix.len() > 0);
1050 debug_assert!(localname.len() > 0);
1051 Ok((
1052 Some(unsafe { NcName::from_auto_unchecked(prefix) }),
1053 unsafe { NcName::from_auto_unchecked(localname) },
1054 ))
1055 }
1056}
1057
1058impl NameStr {
1059 #[cfg(feature = "std")]
1064 #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
1065 pub fn to_name(&self) -> Name {
1066 self.into()
1067 }
1068
1069 pub fn split_name(&self) -> Result<(Option<&'_ NcNameStr>, &'_ NcNameStr), Error> {
1097 let name = &self.0;
1098 let colon_pos = match name.find(':') {
1099 None => return Ok((None, unsafe { NcNameStr::from_str_unchecked(name) })),
1100 Some(pos) => pos,
1101 };
1102 if colon_pos == 0 || colon_pos == name.len() - 1 {
1103 return Err(Error::EmptyNamePart);
1104 }
1105
1106 let (prefix, localname) = name.split_at(colon_pos);
1107 let localname = &localname[1..];
1108
1109 if localname.find(':').is_some() {
1110 return Err(Error::MultiColonName);
1112 };
1113 if !selectors::CLASS_XML_NAMESTART.select(localname.chars().next().unwrap()) {
1114 return Err(Error::InvalidLocalName);
1116 }
1117
1118 debug_assert!(prefix.len() > 0);
1119 debug_assert!(localname.len() > 0);
1120 Ok((
1121 Some(unsafe { NcNameStr::from_str_unchecked(prefix) }),
1122 unsafe { NcNameStr::from_str_unchecked(localname) },
1123 ))
1124 }
1125}
1126
1127#[cfg(feature = "std")]
1128#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
1129impl From<NcName> for Name {
1130 fn from(other: NcName) -> Self {
1131 other.into_name()
1132 }
1133}
1134
1135impl<'x> From<&'x NcNameStr> for &'x NameStr {
1136 fn from(other: &'x NcNameStr) -> Self {
1137 other.as_namestr()
1138 }
1139}
1140
1141rxml_custom_string_type_pair! {
1142 pub struct NcName(CompactString) use validate_ncname;
1164
1165 pub struct NcNameStr(str);
1178}
1179
1180rxml_splitting_impls! {
1181 NcName => selectors::CLASS_XML_NAMESTART => NcNameStr
1182}
1183
1184#[cfg(feature = "std")]
1185#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
1186impl NcName {
1187 pub fn add_suffix(self, suffix: &NcNameStr) -> Name {
1205 let mut s: String = self.0.into();
1206 s.reserve(suffix.len() + 1);
1207 s.push_str(":");
1208 s.push_str(suffix);
1209 unsafe { Name::from_string_unchecked(s) }
1212 }
1213
1214 pub fn into_name(self) -> Name {
1221 unsafe { Name::from_auto_unchecked(self.0) }
1223 }
1224}
1225
1226#[cfg(feature = "std")]
1227#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
1228impl AsRef<NameStr> for NcName {
1229 fn as_ref(&self) -> &NameStr {
1230 <Self as AsRef<NcNameStr>>::as_ref(self).as_ref()
1231 }
1232}
1233
1234#[cfg(feature = "std")]
1235#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
1236impl Borrow<NameStr> for NcName {
1237 fn borrow(&self) -> &NameStr {
1238 <Self as Borrow<NcNameStr>>::borrow(self).borrow()
1239 }
1240}
1241
1242impl NcNameStr {
1243 #[cfg(feature = "std")]
1248 #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
1249 pub fn to_ncname(&self) -> NcName {
1250 self.into()
1251 }
1252
1253 #[cfg(feature = "std")]
1255 #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
1256 pub fn to_name(&self) -> Name {
1257 self.to_ncname().into()
1258 }
1259
1260 pub fn as_namestr<'x>(&'x self) -> &'x NameStr {
1264 unsafe { NameStr::from_str_unchecked(&self.0) }
1266 }
1267
1268 #[cfg(feature = "std")]
1286 #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
1287 pub fn with_suffix(&self, suffix: &NcNameStr) -> Name {
1288 let mut s = String::with_capacity(self.len() + 1 + suffix.len());
1289 s.push_str(self);
1290 s.push_str(":");
1291 s.push_str(suffix);
1292 unsafe { Name::from_string_unchecked(s) }
1295 }
1296}
1297
1298impl AsRef<NameStr> for NcNameStr {
1299 fn as_ref(&self) -> &NameStr {
1300 unsafe { NameStr::from_str_unchecked(&self.0) }
1302 }
1303}
1304
1305impl Borrow<NameStr> for NcNameStr {
1306 fn borrow(&self) -> &NameStr {
1307 self.as_ref()
1308 }
1309}
1310
1311#[cfg(test)]
1312mod tests {
1313 use super::*;
1314
1315 #[test]
1316 fn split_name_on_namestr_with_valid_name() {
1317 let nm: &NameStr = "foo:bar".try_into().unwrap();
1318 let (prefix, localname) = nm.split_name().unwrap();
1319 assert_eq!(prefix.unwrap(), "foo");
1320 assert_eq!(localname, "bar");
1321 }
1322
1323 #[test]
1324 fn split_name_on_namestr_with_prefixless_name() {
1325 let nm: &NameStr = "bar".try_into().unwrap();
1326 let (prefix, localname) = nm.split_name().unwrap();
1327 assert_eq!(prefix, None);
1328 assert_eq!(localname, "bar");
1329 }
1330
1331 #[test]
1332 fn split_name_on_namestr_rejects_localname_with_non_namestart_first_char() {
1333 let nm: &NameStr = "foo:-bar".try_into().unwrap();
1334 let result = nm.split_name();
1335 assert!(matches!(result.err().unwrap(), Error::InvalidLocalName,));
1336 }
1337
1338 #[test]
1339 #[cfg(feature = "std")]
1340 fn split_name_on_name_with_valid_name() {
1341 let nm: Name = "foo:bar".try_into().unwrap();
1342 let (prefix, localname) = nm.split_name().unwrap();
1343 assert_eq!(prefix.unwrap(), "foo");
1344 assert_eq!(localname, "bar");
1345 }
1346
1347 #[test]
1348 #[cfg(feature = "std")]
1349 fn split_name_on_name_with_prefixless_name() {
1350 let nm: Name = "bar".try_into().unwrap();
1351 let (prefix, localname) = nm.split_name().unwrap();
1352 assert_eq!(prefix, None);
1353 assert_eq!(localname, "bar");
1354 }
1355
1356 #[test]
1357 #[cfg(feature = "std")]
1358 fn split_name_on_name_rejects_localname_with_non_namestart_first_char() {
1359 let nm: Name = "foo:-bar".try_into().unwrap();
1360 let result = nm.split_name();
1361 assert!(matches!(result.err().unwrap(), Error::InvalidLocalName,));
1362 }
1363
1364 #[test]
1365 fn split_namestr_on_name_with_valid_name() {
1366 let nm: &NameStr = "foo:bar".try_into().unwrap();
1367 let (prefix, localname) = nm.split_name().unwrap();
1368 assert_eq!(prefix.unwrap(), "foo");
1369 assert_eq!(localname, "bar");
1370 }
1371
1372 #[test]
1373 fn split_namestr_on_name_with_prefixless_name() {
1374 let nm: &NameStr = "bar".try_into().unwrap();
1375 let (prefix, localname) = nm.split_name().unwrap();
1376 assert_eq!(prefix, None);
1377 assert_eq!(localname, "bar");
1378 }
1379
1380 #[test]
1381 fn split_namestr_on_name_rejects_localname_with_non_namestart_first_char() {
1382 let nm: &NameStr = "foo:-bar".try_into().unwrap();
1383 let result = nm.split_name();
1384 assert!(matches!(result.err().unwrap(), Error::InvalidLocalName,));
1385 }
1386
1387 #[test]
1388 #[should_panic(expected = "slice is not a valid NameStr")]
1389 fn namestr_slice_panics_on_non_name_start() {
1390 let x: &NameStr = "foo-bar".try_into().unwrap();
1391 let _: &NameStr = &x[3..];
1392 }
1393
1394 #[test]
1395 #[should_panic(expected = "slice is not a valid NameStr")]
1396 #[cfg(feature = "std")]
1397 fn name_slice_panics_on_non_name_start() {
1398 let x: Name = "foo-bar".try_into().unwrap();
1399 let _: &NameStr = &x[3..];
1400 }
1401
1402 #[test]
1403 #[should_panic(expected = "split string is not a valid Name")]
1404 #[cfg(feature = "std")]
1405 fn name_split_off_refuses_empty_lhs() {
1406 let mut x: Name = "foobar".try_into().unwrap();
1407 x.split_off(0);
1408 }
1409
1410 #[test]
1411 #[should_panic(expected = "split string is not a valid Name")]
1412 #[cfg(feature = "std")]
1413 fn name_split_off_refuses_empty_rhs() {
1414 let mut x: Name = "foobar".try_into().unwrap();
1415 x.split_off(6);
1416 }
1417
1418 #[test]
1419 #[should_panic(expected = "slice is not a valid NcNameStr")]
1420 fn ncnamestr_slice_panics_on_non_name_start() {
1421 let x: &NcNameStr = "foo-bar".try_into().unwrap();
1422 let _: &NcNameStr = &x[3..];
1423 }
1424
1425 #[test]
1426 #[should_panic(expected = "slice is not a valid NcNameStr")]
1427 #[cfg(feature = "std")]
1428 fn ncname_slice_panics_on_non_name_start() {
1429 let x: NcName = "foo-bar".try_into().unwrap();
1430 let _: &NcNameStr = &x[3..];
1431 }
1432
1433 #[test]
1434 fn ncname_refuses_empty_slice() {
1435 match <&str as TryInto<&NcNameStr>>::try_into("") {
1436 Err(_) => (),
1437 other => panic!("unexpected result: {:?}", other),
1438 }
1439 }
1440}