1use std::ops::{
2 Index, Range,
3 RangeFrom, RangeTo,
4 RangeFull,
5};
6use std::cmp::PartialEq;
7use std::default::Default;
8use std::fmt::{self, Display};
9use std::borrow::{ToOwned, Cow};
10use std::ffi::{OsString, OsStr};
11use std::path::Path;
12use std::net::{ToSocketAddrs, SocketAddr};
13use std::str::{self, FromStr, EncodeUtf16};
14use std::{vec, io};
15use std::iter::{Iterator, DoubleEndedIterator};
16
17#[allow(warnings)]
21use std::ascii::AsciiExt;
22
23use error::FromSourceError;
24use soft_char::SoftAsciiChar;
25use soft_string::SoftAsciiString;
26
27#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
48#[repr(transparent)]
53pub struct SoftAsciiStr(str);
54
55
56impl SoftAsciiStr {
57
58 #[inline(always)]
59 pub fn from_unchecked(s: &str) -> &SoftAsciiStr {
60 unsafe { &*( s as *const str as *const SoftAsciiStr) }
61 }
62
63 #[inline(always)]
64 #[deprecated(since = "1.0.0", note="use from_unchecked")]
65 pub fn from_str_unchecked(s: &str) -> &SoftAsciiStr {
66 SoftAsciiStr::from_unchecked(s)
67 }
68
69 #[inline(always)]
70 pub fn from_unchecked_mut(s: &mut str) -> &mut SoftAsciiStr {
71 unsafe { &mut *( s as *mut str as *mut SoftAsciiStr) }
72 }
73
74 pub fn from_str(source: &str) -> Result<&Self, FromSourceError<&str>> {
75 if source.is_ascii() {
76 Ok(Self::from_unchecked(source))
77 } else {
78 Err(FromSourceError::new(source))
79 }
80 }
81
82 pub fn revalidate_soft_constraint(&self) -> Result<&Self, FromSourceError<&str>> {
84 if self.is_ascii() {
85 Ok(self)
86 } else {
87 Err(FromSourceError::new(self.as_str()))
88 }
89 }
90
91 #[inline(always)]
92 pub fn as_str(&self) -> &str {
93 &self.0
94 }
95
96 pub fn into_soft_ascii_string(self: Box<SoftAsciiStr>) -> SoftAsciiString {
97 let as_str = Self::into_boxed_str(self);
100 let string = str::into_string(as_str);
101 SoftAsciiString::from_unchecked(string)
102 }
103
104 pub fn from_boxed_str(bs: Box<str>) -> Box<SoftAsciiStr> {
105 unsafe { Box::from_raw(Box::into_raw(bs) as *mut SoftAsciiStr) }
106 }
107
108 #[inline]
109 pub fn into_boxed_str(self: Box<SoftAsciiStr>) -> Box<str> {
110 unsafe { Box::from_raw(Box::into_raw(self) as *mut str) }
111 }
112
113 #[inline]
114 pub fn lines(&self) -> SoftAsciiLines {
115 SoftAsciiLines::from(self)
116 }
117
118 #[inline]
119 pub fn split_whitespace(&self) -> SoftAsciiSplitWhitespace {
120 SoftAsciiSplitWhitespace::from(self)
121 }
122
123 #[inline]
124 pub fn char_indices(&self) -> SoftAsciiCharIndices {
125 SoftAsciiCharIndices::from(self)
126 }
127
128 #[inline]
129 pub fn chars(&self) -> SoftAsciiChars {
130 SoftAsciiChars::from(self)
131 }
132
133 pub fn split_at(&self, mid: usize) -> (&SoftAsciiStr, &SoftAsciiStr) {
134 let (left, right) = self.as_str().split_at(mid);
135 (SoftAsciiStr::from_unchecked(left), SoftAsciiStr::from_unchecked(right))
136 }
137
138 #[deprecated(since="1.1.0", note="deprecated in std")]
139 pub unsafe fn slice_unchecked(&self, begin: usize, end: usize) -> &SoftAsciiStr {
140 #[allow(deprecated)]
141 SoftAsciiStr::from_unchecked(self.as_str().slice_unchecked(begin, end))
142 }
143
144 pub unsafe fn get_unchecked<I>(&self, index: I) -> &SoftAsciiStr
161 where
162 I: hidden::TempSliceIndexHelper
163 {
164 SoftAsciiStr::from_unchecked(self.as_str().get_unchecked::<I>(index))
165 }
166
167
168
169 pub fn inner_str_mut(&mut self) -> &mut str {
178 &mut self.0
179 }
180
181 pub fn parse<F>(&self) -> Result<F, <F as FromStr>::Err>
182 where F: FromStr
183 {
184 self.as_str().parse::<F>()
185 }
186}
187
188mod hidden {
189 use std::slice::SliceIndex;
190 use std::ops::{Range, RangeFrom, RangeTo, RangeFull, RangeToInclusive, RangeInclusive};
191
192 pub trait TempSliceIndexHelper: SliceIndex<str, Output=str> {}
203
204 impl TempSliceIndexHelper for Range<usize> {}
205 impl TempSliceIndexHelper for RangeInclusive<usize> {}
206 impl TempSliceIndexHelper for RangeFrom<usize> {}
207 impl TempSliceIndexHelper for RangeTo<usize> {}
208 impl TempSliceIndexHelper for RangeToInclusive<usize> {}
209 impl TempSliceIndexHelper for RangeFull {}
210
211}
212
213macro_rules! impl_wrap_returning_string {
216 (pub > $(fn $name:ident(&self$(, $param:ident: $tp:ty)*)),*) => ($(
217 impl SoftAsciiStr {
218 #[inline]
219 pub fn $name(&self $(, $param: $tp)*) -> SoftAsciiString {
220 let as_str = self.as_str();
221 let res = str::$name(as_str $(, $param)*);
222 SoftAsciiString::from_unchecked(res)
223 }
224 }
225 )*)
226}
227
228impl_wrap_returning_string!{
229 pub >
230 fn to_lowercase(&self),
231 fn to_uppercase(&self),
232 fn repeat(&self, n: usize)
233}
234
235macro_rules! impl_wrap_returning_str {
236 (pub > $(
237 $(#[$attr:meta])*
238 fn $name:ident(&self$(, $param:ident: $tp:ty)*)
239 $(#[$inner_attr:meta])*
240 ),*) => (
241 impl SoftAsciiStr {$(
242 $(#[$attr])*
243 #[inline]
244 pub fn $name(&self $(, $param: $tp)*) -> &SoftAsciiStr {
245 let as_str = self.as_str();
246 $(#[$inner_attr])* {
247 let res = str::$name(as_str $(, $param)*);
248 SoftAsciiStr::from_unchecked(res)
249 }
250 }
251 )*}
252 );
253}
254
255impl_wrap_returning_str!{
256 pub >
257 #[deprecated(since="1.1.0", note="deprecated in std")]
258 fn trim_right(&self) #[allow(deprecated)],
259 #[deprecated(since="1.1.0", note="deprecated in std")]
260 fn trim_left(&self) #[allow(deprecated)],
261 fn trim_end(&self),
262 fn trim_start(&self),
263 fn trim(&self)
264}
265
266macro_rules! impl_wrapping {
267 (pub > $(fn $name:ident(&self$(, $param:ident: $tp:ty)*) -> $ret:ty),*) => (
268 impl SoftAsciiStr {$(
269 #[inline]
270 pub fn $name(&self $(, $param: $tp)*) -> $ret {
271 str::$name(self.as_str() $(, $param)*)
272 }
273 )*}
274 )
275}
276
277impl_wrapping! {
278 pub >
279 fn len(&self) -> usize,
280 fn is_empty(&self) -> bool,
281 fn is_char_boundary(&self, index: usize) -> bool,
282 fn as_ptr(&self) -> *const u8,
283 fn encode_utf16(&self) -> EncodeUtf16,
284 fn is_ascii(&self) -> bool,
285 fn as_bytes(&self) -> &[u8]
286}
287
288impl AsRef<SoftAsciiStr> for SoftAsciiStr {
289 #[inline]
290 fn as_ref(&self) -> &Self {
291 self
292 }
293}
294impl AsRef<str> for SoftAsciiStr {
295 #[inline]
296 fn as_ref(&self) -> &str {
297 &self.0
298 }
299}
300
301impl AsRef<[u8]> for SoftAsciiStr {
302 #[inline]
303 fn as_ref(&self) -> &[u8] {
304 self.as_bytes()
305 }
306}
307
308impl<'a> Default for &'a SoftAsciiStr {
309 #[inline]
310 fn default() -> &'a SoftAsciiStr {
311 SoftAsciiStr::from_unchecked(Default::default())
312 }
313}
314
315impl Display for SoftAsciiStr {
316 fn fmt(&self, fter: &mut fmt::Formatter) -> fmt::Result {
317 self.0.fmt(fter)
318 }
319}
320
321macro_rules! impl_index {
322 ($($idx:ty),*) => ($(
323 impl Index<$idx> for SoftAsciiStr {
324 type Output = SoftAsciiStr;
325 fn index(&self, index: $idx) -> &Self::Output {
326 SoftAsciiStr::from_unchecked(self.0.index(index))
327 }
328 }
329 )*);
330}
331
332impl_index! {
333 Range<usize>,
334 RangeFrom<usize>,
335 RangeTo<usize>,
336 RangeFull
337}
338
339impl ToOwned for SoftAsciiStr {
340 type Owned = SoftAsciiString;
341
342 fn to_owned(&self) -> SoftAsciiString {
343 SoftAsciiString::from_unchecked(String::from(&self.0))
344 }
345}
346
347impl PartialEq<SoftAsciiString> for SoftAsciiStr {
348 fn eq(&self, other: &SoftAsciiString) -> bool {
349 self == &**other
350 }
351}
352
353impl<'a> PartialEq<SoftAsciiString> for &'a SoftAsciiStr {
354 fn eq(&self, other: &SoftAsciiString) -> bool {
355 *self == &**other
356 }
357}
358
359impl PartialEq<SoftAsciiStr> for String {
360 fn eq(&self, other: &SoftAsciiStr) -> bool {
361 self.as_str() == other.as_str()
362 }
363}
364
365impl PartialEq<String> for SoftAsciiStr {
366 fn eq(&self, other: &String) -> bool {
367 self.as_str() == other.as_str()
368 }
369}
370
371impl<'a> PartialEq<&'a SoftAsciiStr> for String {
372 fn eq(&self, other: &&'a SoftAsciiStr) -> bool {
373 self.as_str() == other.as_str()
374 }
375}
376
377impl<'a> PartialEq<String> for &'a SoftAsciiStr {
378 fn eq(&self, other: &String) -> bool {
379 self.as_str() == other.as_str()
380 }
381}
382
383impl<'a> PartialEq<SoftAsciiStr> for str {
384 fn eq(&self, other: &SoftAsciiStr) -> bool {
385 self == other.as_str()
386 }
387}
388
389impl PartialEq<str> for SoftAsciiStr {
390 fn eq(&self, other: &str) -> bool {
391 self.as_str() == other
392 }
393}
394
395impl<'a> PartialEq<SoftAsciiStr> for Cow<'a, SoftAsciiStr> {
396 fn eq(&self, other: &SoftAsciiStr) -> bool {
397 self.as_str() == other.as_str()
398 }
399}
400
401impl<'a> PartialEq<Cow<'a, SoftAsciiStr>> for SoftAsciiStr {
402 fn eq(&self, other: &Cow<'a, SoftAsciiStr>) -> bool {
403 self.as_str() == other.as_str()
404 }
405}
406
407impl<'a, 'b> PartialEq<&'b SoftAsciiStr> for Cow<'a, SoftAsciiStr> {
408 fn eq(&self, other: &&'b SoftAsciiStr) -> bool {
409 self.as_str() == other.as_str()
410 }
411}
412
413impl<'a, 'b> PartialEq<Cow<'a, SoftAsciiStr>> for &'a SoftAsciiStr {
414 fn eq(&self, other: &Cow<'a, SoftAsciiStr>) -> bool {
415 self.as_str() == other.as_str()
416 }
417}
418
419impl<'a> PartialEq<SoftAsciiStr> for Cow<'a, str> {
420 fn eq(&self, other: &SoftAsciiStr) -> bool {
421 &*self == other.as_str()
422 }
423}
424
425impl<'a> PartialEq<Cow<'a, str>> for SoftAsciiStr {
426 fn eq(&self, other: &Cow<'a, str>) -> bool {
427 self.as_str() == &*other
428 }
429}
430
431impl<'a, 'b> PartialEq<&'b SoftAsciiStr> for Cow<'a, str> {
432 fn eq(&self, other: &&'b SoftAsciiStr) -> bool {
433 &*self == other.as_str()
434 }
435}
436
437impl<'a, 'b> PartialEq<Cow<'b, str>> for &'a SoftAsciiStr {
438 fn eq(&self, other: &Cow<'b, str>) -> bool {
439 self.as_str() == &*other
440 }
441}
442
443impl PartialEq<SoftAsciiStr> for OsString {
444 fn eq(&self, other: &SoftAsciiStr) -> bool {
445 other.as_str().eq(self)
446 }
447}
448
449impl PartialEq<OsString> for SoftAsciiStr {
450 fn eq(&self, other: &OsString) -> bool {
451 self.as_str().eq(other)
452 }
453}
454
455impl<'a> PartialEq<&'a SoftAsciiStr> for OsString {
456 fn eq(&self, other: &&'a SoftAsciiStr) -> bool {
457 other.as_str().eq(self)
458 }
459}
460
461impl<'a> PartialEq<OsString> for &'a SoftAsciiStr {
462 fn eq(&self, other: &OsString) -> bool {
463 self.as_str().eq(other)
464 }
465}
466
467impl PartialEq<SoftAsciiStr> for OsStr {
468 fn eq(&self, other: &SoftAsciiStr) -> bool {
469 other.as_str().eq(self)
470 }
471}
472impl PartialEq<OsStr> for SoftAsciiStr {
473 fn eq(&self, other: &OsStr) -> bool {
474 self.as_str().eq(other)
475 }
476}
477
478impl AsRef<OsStr> for SoftAsciiStr {
479 fn as_ref(&self) -> &OsStr {
480 self.as_str().as_ref()
481 }
482}
483
484impl AsRef<Path> for SoftAsciiStr {
485 fn as_ref(&self) -> &Path {
486 self.as_str().as_ref()
487 }
488}
489
490impl ToSocketAddrs for SoftAsciiStr {
491 type Iter = vec::IntoIter<SocketAddr>;
492
493 fn to_socket_addrs(&self) -> io::Result<vec::IntoIter<SocketAddr>> {
494 self.as_str().to_socket_addrs()
495 }
496}
497
498#[derive(Debug, Clone)]
503pub struct SoftAsciiChars<'a> {
504 inner: str::Chars<'a>
505}
506
507impl<'a> From<&'a SoftAsciiStr> for SoftAsciiChars<'a> {
508 fn from(s: &'a SoftAsciiStr) -> SoftAsciiChars<'a> {
509 SoftAsciiChars {
510 inner: s.as_str().chars()
511 }
512 }
513}
514
515impl<'a> Iterator for SoftAsciiChars<'a> {
516 type Item = SoftAsciiChar;
517
518 fn next(&mut self) -> Option<Self::Item> {
519 self.inner.next()
520 .map(SoftAsciiChar::from_unchecked)
521
522 }
523
524 #[inline]
525 fn count(self) -> usize {
526 self.inner.count()
527 }
528
529 #[inline]
530 fn size_hint(&self) -> (usize, Option<usize>) {
531 self.inner.size_hint()
532 }
533
534 fn last(self) -> Option<Self::Item> {
535 self.inner.last()
536 .map(SoftAsciiChar::from_unchecked)
537 }
538}
539
540impl<'a> DoubleEndedIterator for SoftAsciiChars<'a> {
541 fn next_back(&mut self) -> Option<Self::Item> {
542 self.inner.next_back()
543 .map(SoftAsciiChar::from_unchecked)
544 }
545}
546
547#[derive(Debug, Clone)]
552pub struct SoftAsciiCharIndices<'a> {
553 inner: str::CharIndices<'a>
554}
555
556impl<'a> From<&'a SoftAsciiStr> for SoftAsciiCharIndices<'a> {
557 fn from(s: &'a SoftAsciiStr) -> SoftAsciiCharIndices<'a> {
558 SoftAsciiCharIndices {
559 inner: s.as_str().char_indices()
560 }
561 }
562}
563
564impl<'a> Iterator for SoftAsciiCharIndices<'a> {
565 type Item = (usize, SoftAsciiChar);
566
567 fn next(&mut self) -> Option<Self::Item> {
568 self.inner.next()
569 .map(|(idx, ch)| {
570 (idx, SoftAsciiChar::from_unchecked(ch))
571 })
572 }
573
574 #[inline]
575 fn count(self) -> usize {
576 self.inner.count()
577 }
578
579 #[inline]
580 fn size_hint(&self) -> (usize, Option<usize>) {
581 self.inner.size_hint()
582 }
583
584 fn last(self) -> Option<Self::Item> {
585 self.inner.last()
586 .map(|(idx, ch)| {
587 (idx, SoftAsciiChar::from_unchecked(ch))
588 })
589 }
590}
591
592impl<'a> DoubleEndedIterator for SoftAsciiCharIndices<'a> {
593 fn next_back(&mut self) -> Option<Self::Item> {
594 self.inner.next_back()
595 .map(|(idx, ch)| {
596 (idx, SoftAsciiChar::from_unchecked(ch))
597 })
598 }
599}
600
601#[derive(Debug, Clone)]
606pub struct SoftAsciiLines<'a> {
607 inner: str::Lines<'a>
608}
609
610impl<'a> From<&'a SoftAsciiStr> for SoftAsciiLines<'a> {
611 fn from(s: &'a SoftAsciiStr) -> SoftAsciiLines<'a> {
612 SoftAsciiLines {
613 inner: s.as_str().lines()
614 }
615 }
616}
617
618impl<'a> Iterator for SoftAsciiLines<'a> {
619 type Item = &'a SoftAsciiStr;
620
621 fn next(&mut self) -> Option<Self::Item> {
622 self.inner.next()
623 .map(SoftAsciiStr::from_unchecked)
624 }
625
626 fn size_hint(&self) -> (usize, Option<usize>) {
627 self.inner.size_hint()
628 }
629
630}
631
632impl<'a> DoubleEndedIterator for SoftAsciiLines<'a> {
633 fn next_back(&mut self) -> Option<Self::Item> {
634 self.inner.next_back()
635 .map(SoftAsciiStr::from_unchecked)
636 }
637}
638
639#[derive(Clone)]
644pub struct SoftAsciiSplitWhitespace<'a> {
645 inner: str::SplitWhitespace<'a>
646}
647
648
649impl<'a> From<&'a SoftAsciiStr> for SoftAsciiSplitWhitespace<'a> {
650 fn from(s: &'a SoftAsciiStr) -> SoftAsciiSplitWhitespace<'a> {
651 SoftAsciiSplitWhitespace {
652 inner: s.as_str().split_whitespace()
653 }
654 }
655}
656
657impl<'a> Iterator for SoftAsciiSplitWhitespace<'a> {
658 type Item = &'a SoftAsciiStr;
659
660 fn next(&mut self) -> Option<Self::Item> {
661 self.inner.next()
662 .map(SoftAsciiStr::from_unchecked)
663 }
664}
665
666impl<'a> DoubleEndedIterator for SoftAsciiSplitWhitespace<'a> {
667 fn next_back(&mut self) -> Option<Self::Item> {
668 self.inner.next_back()
669 .map(SoftAsciiStr::from_unchecked)
670 }
671}
672
673
674#[cfg(test)]
675mod test {
676 const UTF8_STR: &str = "❤ == <3";
677 mod SoftAsciiStr {
682 #![allow(non_snake_case)]
683 use super::*;
684 use super::super::SoftAsciiStr;
685 use std::ops::{Range, RangeInclusive, RangeFrom, RangeTo, RangeToInclusive, RangeFull};
686
687 #[test]
688 fn from_str() {
689 assert_eq!(
690 SoftAsciiStr::from_str("hy ho\x00\x01\x02\x03").unwrap(),
691 "hy ho\x00\x01\x02\x03"
692 );
693 assert!(SoftAsciiStr::from_str("↓").is_err());
694 }
695
696 #[test]
697 fn from_unchecked() {
698 assert_eq!(
699 SoftAsciiStr::from_unchecked(UTF8_STR),
700 UTF8_STR
701 );
702 }
703
704 #[test]
705 fn revalidate_soft_constraint() {
706 let res = SoftAsciiStr::from_unchecked(UTF8_STR).revalidate_soft_constraint();
707 assert_eq!(UTF8_STR, assert_err!(res).into_source());
708
709 let res = SoftAsciiStr::from_unchecked("hy").revalidate_soft_constraint();
710 let res: &SoftAsciiStr = assert_ok!(res);
711 assert_eq!(
712 res,
713 "hy"
714 );
715
716 }
717
718 #[test]
719 fn compile_bounds__get_unchecked() {
720 let _ = SoftAsciiStr::get_unchecked::<Range<usize>>;
721 let _ = SoftAsciiStr::get_unchecked::<RangeInclusive<usize>>;
722 let _ = SoftAsciiStr::get_unchecked::<RangeFrom<usize>>;
723 let _ = SoftAsciiStr::get_unchecked::<RangeTo<usize>>;
724 let _ = SoftAsciiStr::get_unchecked::<RangeToInclusive<usize>>;
725 let _ = SoftAsciiStr::get_unchecked::<RangeFull>;
726 }
727 }
728
729}