soft_ascii_string/
soft_string.rs

1use std::borrow::{Cow, Borrow};
2use std::ops::{Deref, DerefMut, AddAssign, Add};
3use std::cmp::PartialEq;
4use std::iter::{IntoIterator, FromIterator, Extend};
5use std::ops::{
6    Index, IndexMut,
7    Range, RangeFrom,
8    RangeTo, RangeFull,
9};
10use std::path::Path;
11use std::ffi::OsStr;
12use std::net::{ToSocketAddrs, SocketAddr};
13use std::fmt::{self, Display};
14use std::{io, vec};
15use std::str::FromStr;
16
17// this import will become unused in future rust versions
18// but won't be removed for now for supporting current
19// rust versions
20#[allow(warnings)]
21use std::ascii::AsciiExt;
22
23use error::{StringFromStrError, FromSourceError};
24use soft_char::SoftAsciiChar;
25use soft_str::SoftAsciiStr;
26
27/// a `String` wrapper with an additional "is us-ascii" soft constraint
28#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
29pub struct SoftAsciiString(String);
30
31impl SoftAsciiString {
32
33    #[inline(always)]
34    pub fn from_unchecked<S: Into<String>>(s: S) -> Self {
35        SoftAsciiString(s.into())
36    }
37
38    #[inline(always)]
39    #[deprecated(since="1.0.0", note="use from_unchecked")]
40    pub fn from_string_unchecked<S: Into<String>>(s: S) -> Self {
41        SoftAsciiString::from_unchecked(s)
42    }
43
44    pub fn from_string<S>(source: S) -> Result<Self, FromSourceError<S>>
45        where S: fmt::Debug + AsRef<str> + Into<String>
46    {
47        if source.as_ref().is_ascii() {
48            Ok(Self::from_unchecked(source))
49        } else {
50            Err(FromSourceError::new(source))
51        }
52    }
53
54    #[inline]
55    pub fn new() -> Self {
56        Default::default()
57    }
58
59    #[inline]
60    pub fn with_capacity(cap: usize) -> Self {
61        SoftAsciiString(String::with_capacity(cap))
62    }
63
64
65    pub fn revalidate_soft_constraint(self) -> Result<SoftAsciiString, String> {
66        if self.is_ascii() {
67            Ok(self)
68        } else {
69            Err(self.0)
70        }
71    }
72
73    //TODO warn in doc
74    #[inline]
75    pub fn inner_string_mut(&mut self) -> &mut String {
76        &mut self.0
77    }
78
79    #[inline]
80    pub fn inner_string(&self) -> &String {
81        &self.0
82    }
83
84    #[inline]
85    pub fn push_str(&mut self, other: &SoftAsciiStr) {
86        self.0.push_str(other.as_str())
87    }
88
89    #[inline]
90    pub fn into_bytes(self) -> Vec<u8> {
91        self.0.into_bytes()
92    }
93
94    #[inline]
95    pub fn push(&mut self, ch: SoftAsciiChar) {
96        self.0.push(ch.into())
97    }
98
99    pub fn pop(&mut self) -> Option<SoftAsciiChar> {
100        self.0.pop()
101            .map(SoftAsciiChar::from_unchecked)
102    }
103
104    pub fn remove(&mut self, idx: usize) -> SoftAsciiChar {
105        SoftAsciiChar::from_unchecked(self.0.remove(idx))
106    }
107
108    #[inline]
109    pub fn insert(&mut self, idx: usize, ch: SoftAsciiChar) {
110        self.0.insert(idx, ch.into())
111    }
112
113    #[inline]
114    pub fn insert_str(&mut self, idx: usize, string: &SoftAsciiStr) {
115        self.0.insert_str(idx, string.as_str())
116    }
117
118    #[inline]
119    pub fn as_soft_ascii_str(&self) -> &SoftAsciiStr {
120        SoftAsciiStr::from_unchecked(self.0.as_str())
121    }
122
123    #[inline]
124    pub fn as_soft_ascii_str_mut(&mut self) -> &mut SoftAsciiStr {
125        SoftAsciiStr::from_unchecked_mut(self.0.as_mut_str())
126    }
127
128    #[inline]
129    pub fn split_off(&mut self, at: usize) -> SoftAsciiString {
130        SoftAsciiString::from_unchecked(self.0.split_off(at))
131    }
132
133    #[inline]
134    pub fn into_boxed_soft_ascii_str(self) -> Box<SoftAsciiStr> {
135        SoftAsciiStr::from_boxed_str(self.into_boxed_str())
136    }
137
138    #[inline]
139    pub fn into_boxed_str(self) -> Box<str> {
140        self.0.into_boxed_str()
141    }
142
143    #[inline]
144    pub fn is_ascii(&self) -> bool {
145        self.0.is_ascii()
146    }
147
148}
149
150macro_rules! impl_wrapping {
151    (pub > $(fn $name:ident(&self$(, $param:ident: $tp:ty)*) -> $ret:ty),*) => (
152        impl SoftAsciiString {$(
153            #[inline]
154            pub fn $name(&self $(, $param: $tp)*) -> $ret {
155                String::$name(&self.0 $(, $param)*)
156            }
157        )*}
158    )
159}
160
161impl_wrapping! {
162    pub >
163    fn as_bytes(&self) -> &[u8],
164    fn capacity(&self) -> usize,
165    fn len(&self) -> usize,
166    fn as_str(&self) -> &str,
167    fn is_empty(&self) -> bool
168}
169
170macro_rules! impl_wrapping_mut {
171    (pub > $(fn $name:ident(&mut self$(, $param:ident: $tp:ty)*) -> $ret:ty),*) => (
172        impl SoftAsciiString {$(
173            #[inline]
174            pub fn $name(&mut self $(, $param: $tp)*) -> $ret {
175                String::$name(&mut self.0 $(, $param)*)
176            }
177        )*}
178    )
179}
180
181impl_wrapping_mut! {
182    pub >
183    fn reserve(&mut self, additional: usize) -> (),
184    fn reserve_exact(&mut self, additional: usize) -> (),
185    fn shrink_to_fit(&mut self) -> (),
186    fn truncate(&mut self, new_len: usize) -> (),
187    fn clear(&mut self) -> ()
188}
189
190impl Borrow<str> for SoftAsciiString {
191    #[inline]
192    fn borrow(&self) -> &str {
193        self.as_str()
194    }
195}
196
197impl Borrow<SoftAsciiStr> for SoftAsciiString {
198    fn borrow(&self) -> &SoftAsciiStr {
199        &*self
200    }
201}
202
203impl Deref for SoftAsciiString {
204    type Target = SoftAsciiStr;
205
206    #[inline]
207    fn deref(&self) -> &Self::Target {
208        self.as_soft_ascii_str()
209    }
210}
211
212impl DerefMut for SoftAsciiString {
213    #[inline]
214    fn deref_mut(&mut self) -> &mut Self::Target {
215        self.as_soft_ascii_str_mut()
216    }
217}
218
219
220impl<'a> AddAssign<&'a SoftAsciiStr> for SoftAsciiString {
221    fn add_assign(&mut self,  other: &'a SoftAsciiStr) {
222        self.push_str(other)
223    }
224}
225impl<'a> Add<&'a SoftAsciiStr> for SoftAsciiString {
226    type Output = Self;
227
228    fn add(mut self, other: &'a SoftAsciiStr) -> Self {
229        self.push_str(other);
230        self
231    }
232}
233
234impl PartialEq<SoftAsciiString> for str {
235    fn eq(&self, other: &SoftAsciiString) -> bool {
236        self == other.as_str()
237    }
238}
239
240impl<'a> PartialEq<&'a str> for SoftAsciiString {
241    fn eq(&self, other: &&'a str) -> bool {
242        self.as_str() == *other
243    }
244}
245
246impl<'a> PartialEq<SoftAsciiString> for &'a str {
247    fn eq(&self, other: &SoftAsciiString) -> bool {
248        other.as_str() == *self
249    }
250}
251
252impl PartialEq<str> for SoftAsciiString {
253    #[inline]
254    fn eq(&self, other: &str) -> bool {
255        self.as_str() == other
256    }
257}
258
259
260impl PartialEq<String> for SoftAsciiString {
261    #[inline]
262    fn eq(&self, other: &String) -> bool {
263        self.as_str() == other.as_str()
264    }
265}
266
267impl PartialEq<SoftAsciiString> for String {
268    fn eq(&self, other: &SoftAsciiString) -> bool {
269        self == other.as_str()
270    }
271}
272
273impl<'a> PartialEq<&'a SoftAsciiStr> for SoftAsciiString {
274    #[inline]
275    fn eq(&self, other: &&'a SoftAsciiStr) -> bool {
276        self.as_str() == other.as_str()
277    }
278}
279
280impl<'a> PartialEq<SoftAsciiString> for Cow<'a, str> {
281    #[inline]
282    fn eq(&self, other: &SoftAsciiString) -> bool {
283        other.as_str() == &*self
284    }
285}
286
287impl<'a> PartialEq<Cow<'a, str>> for SoftAsciiString {
288    #[inline]
289    fn eq(&self, other: &Cow<'a, str>) -> bool {
290        self.as_str() == &*other
291    }
292}
293
294impl<'a> PartialEq<SoftAsciiString> for Cow<'a, SoftAsciiStr> {
295    #[inline]
296    fn eq(&self, other: &SoftAsciiString) -> bool {
297        self.as_str() == other.as_str()
298    }
299}
300
301impl<'a> PartialEq<Cow<'a, SoftAsciiStr>> for SoftAsciiString {
302    #[inline]
303    fn eq(&self, other: &Cow<'a, SoftAsciiStr>) -> bool {
304        self.as_str() == other.as_str()
305    }
306}
307
308
309impl FromIterator<SoftAsciiChar> for SoftAsciiString {
310    fn from_iter<I>(iter: I) -> Self
311        where I: IntoIterator<Item=SoftAsciiChar>
312    {
313        let mut buf = SoftAsciiString::new();
314        buf.extend(iter);
315        buf
316    }
317}
318
319impl<'a> FromIterator<&'a SoftAsciiChar> for SoftAsciiString {
320    fn from_iter<I>(iter: I) -> Self
321        where I: IntoIterator<Item=&'a SoftAsciiChar>
322    {
323        let mut buf = SoftAsciiString::new();
324        buf.extend(iter);
325        buf
326    }
327}
328
329impl FromIterator<SoftAsciiString> for SoftAsciiString {
330    fn from_iter<I>(iter: I) -> Self
331        where I: IntoIterator<Item=SoftAsciiString>
332    {
333        let mut buf = SoftAsciiString::new();
334        buf.extend(iter);
335        buf
336    }
337}
338
339impl<'a> FromIterator<Cow<'a, SoftAsciiStr>> for SoftAsciiString {
340    fn from_iter<I>(iter: I) -> Self
341        where I: IntoIterator<Item=Cow<'a, SoftAsciiStr>>
342    {
343        let mut buf = SoftAsciiString::new();
344        buf.extend(iter);
345        buf
346    }
347}
348
349impl<'a> FromIterator<&'a SoftAsciiStr> for SoftAsciiString {
350    fn from_iter<I>(iter: I) -> Self
351        where I: IntoIterator<Item=&'a SoftAsciiStr>
352    {
353        let mut buf = SoftAsciiString::new();
354        buf.extend(iter);
355        buf
356    }
357}
358
359impl AsRef<SoftAsciiStr> for SoftAsciiString {
360    #[inline]
361    fn as_ref(&self) -> &SoftAsciiStr {
362        &*self
363    }
364}
365
366impl AsRef<str> for SoftAsciiString {
367    #[inline]
368    fn as_ref(&self) -> &str {
369        self.as_str()
370    }
371}
372
373impl AsRef<[u8]> for SoftAsciiString {
374    #[inline]
375    fn as_ref(&self) -> &[u8] {
376        self.as_bytes()
377    }
378}
379
380impl AsRef<OsStr> for SoftAsciiString {
381    #[inline]
382    fn as_ref(&self) -> &OsStr {
383        self.0.as_ref()
384    }
385}
386
387impl AsRef<Path> for SoftAsciiString {
388    #[inline]
389    fn as_ref(&self) -> &Path {
390        self.0.as_ref()
391    }
392}
393
394impl ToSocketAddrs for SoftAsciiString {
395    type Iter = vec::IntoIter<SocketAddr>;
396
397    fn to_socket_addrs(&self) -> io::Result<vec::IntoIter<SocketAddr>> {
398        self.as_str().to_socket_addrs()
399    }
400}
401
402macro_rules! impl_index {
403    ($($idx:ty),*) => ($(
404        impl Index<$idx> for SoftAsciiString {
405            type Output = SoftAsciiStr;
406            fn index(&self, index: $idx) -> &Self::Output {
407                SoftAsciiStr::from_unchecked(self.0.index(index))
408            }
409        }
410    )*);
411}
412
413impl_index! {
414    Range<usize>,
415    RangeFrom<usize>,
416    RangeTo<usize>,
417    RangeFull
418}
419
420macro_rules! impl_index_mut {
421    ($($idx:ty),*) => ($(
422        impl IndexMut<$idx> for SoftAsciiString {
423            fn index_mut(&mut self, index: $idx) -> &mut Self::Output {
424                SoftAsciiStr::from_unchecked_mut(self.0.index_mut(index))
425            }
426        }
427    )*);
428}
429
430impl_index_mut! {
431    Range<usize>,
432    RangeFrom<usize>,
433    RangeTo<usize>,
434    RangeFull
435}
436
437impl Extend<SoftAsciiChar> for SoftAsciiString {
438    fn extend<I>(&mut self, iter: I)
439        where I: IntoIterator<Item=SoftAsciiChar>
440    {
441        let iterator = iter.into_iter();
442        let (min, _max) = iterator.size_hint();
443        self.reserve(min);
444        for ch in iterator {
445            self.push(ch)
446        }
447    }
448}
449
450impl<'a> Extend<&'a SoftAsciiChar> for SoftAsciiString {
451    fn extend<I>(&mut self, iter: I)
452        where I: IntoIterator<Item=&'a SoftAsciiChar>
453    {
454        self.extend(iter.into_iter().cloned())
455    }
456}
457
458impl Extend<SoftAsciiString> for SoftAsciiString {
459    fn extend<I>(&mut self, iter: I)
460        where I: IntoIterator<Item=SoftAsciiString>
461    {
462        for string in iter {
463            self.push_str(&*string);
464        }
465    }
466}
467
468impl<'a> Extend<&'a SoftAsciiStr> for SoftAsciiString {
469    fn extend<I>(&mut self, iter: I)
470        where I: IntoIterator<Item=&'a SoftAsciiStr>
471    {
472        for str in iter {
473            self.push_str(str);
474        }
475    }
476}
477
478impl<'a> Extend<Cow<'a, SoftAsciiStr>> for SoftAsciiString {
479    fn extend<I>(&mut self, iter: I)
480        where I: IntoIterator<Item=Cow<'a, SoftAsciiStr>>
481    {
482        for cow in iter {
483            self.push_str(&cow)
484        }
485    }
486}
487
488
489
490
491impl<'a> From<Cow<'a, SoftAsciiStr>> for SoftAsciiString {
492    fn from(cow: Cow<'a, SoftAsciiStr>) -> Self {
493        match cow {
494            Cow::Owned(s) => s,
495            Cow::Borrowed(b) => b.to_owned()
496        }
497    }
498}
499
500impl<'a> From<&'a SoftAsciiStr> for SoftAsciiString {
501    #[inline]
502    fn from(s: &'a SoftAsciiStr) -> Self {
503        s.to_owned()
504    }
505}
506
507impl From<Box<SoftAsciiStr>> for SoftAsciiString {
508    #[inline]
509    fn from(b: Box<SoftAsciiStr>) -> SoftAsciiString {
510        SoftAsciiStr::into_soft_ascii_string(b)
511    }
512}
513
514impl Display for SoftAsciiString {
515    #[inline]
516    fn fmt(&self, fter: &mut fmt::Formatter) -> fmt::Result {
517        self.0.fmt(fter)
518    }
519}
520
521impl Into<Vec<u8>> for SoftAsciiString {
522
523    #[inline]
524    fn into(self) -> Vec<u8> {
525        self.0.into()
526    }
527}
528
529impl Into<String> for SoftAsciiString {
530    #[inline]
531    fn into(self) -> String {
532        self.0
533    }
534}
535
536impl FromStr for SoftAsciiString {
537    type Err = StringFromStrError;
538
539    fn from_str(s: &str) -> Result<Self, Self::Err> {
540        if s.is_ascii() {
541            Ok(SoftAsciiString(s.to_owned()))
542        } else {
543            Err(StringFromStrError)
544        }
545    }
546}
547
548#[cfg(test)]
549mod tests {
550    use std::str;
551    use std::borrow::Borrow;
552    use ::SoftAsciiStr;
553
554    const SOME_NOT_ASCII: &str = "malformed←";
555    const SOME_ASCII: &str = "hy there";
556
557    //FIXME use impl Trait instead
558    fn borrow_untyped<T: ?Sized, B: Borrow<T>>(b: &B) -> &B { b }
559    fn as_ref_untype<T: ?Sized, B: AsRef<T>>(b: &B) -> &B { b }
560
561    mod SoftAsciiString {
562        #![allow(non_snake_case)]
563        use super::*;
564        use super::super::SoftAsciiString;
565
566        #[test]
567        fn from_unchecked() {
568            let sas = SoftAsciiString::from_unchecked(SOME_ASCII);
569            assert_eq!(&*sas, SOME_ASCII);
570            let sas = SoftAsciiString::from_unchecked(SOME_NOT_ASCII);
571            assert_eq!(&*sas, SOME_NOT_ASCII);
572        }
573
574        #[test]
575        fn from_string() {
576            let sas: SoftAsciiString = assert_ok!(SoftAsciiString::from_string(SOME_ASCII));
577            assert_eq!(&*sas, SOME_ASCII);
578                        let sas: SoftAsciiString = assert_ok!(SoftAsciiString::from_string(SOME_ASCII.to_owned()));
579            assert_eq!(&*sas, SOME_ASCII);
580            let failed: String =
581                assert_err!(SoftAsciiString::from_string(SOME_NOT_ASCII.to_owned())).into_source();
582            assert_eq!(&*failed, SOME_NOT_ASCII);
583        }
584
585        #[test]
586        fn borrow_str() {
587            let sas = SoftAsciiString::from_string(SOME_ASCII);
588            let sas = assert_ok!(sas);
589
590            assert_eq!(
591                borrow_untyped::<str, _>(&sas),
592                SOME_ASCII
593            );
594        }
595
596        #[test]
597        fn as_ref_str() {
598            let sas = SoftAsciiString::from_string(SOME_ASCII);
599            let sas = assert_ok!(sas);
600
601            assert_eq!(
602                as_ref_untype::<str, _>(&sas),
603                SOME_ASCII
604            );
605        }
606
607        #[test]
608        fn buffer() {
609            let mut sas = assert_ok!(SoftAsciiString::from_string(SOME_ASCII));
610            {
611                let b: &String = sas.inner_string();
612                assert_eq!(b, &String::from(SOME_ASCII));
613            }
614            {
615                let b: &mut String = sas.inner_string_mut();
616                assert_eq!(b, &mut String::from(SOME_ASCII));
617            }
618        }
619
620        #[test]
621        fn revalidate_soft_constraint() {
622            let sas: SoftAsciiString =
623                SoftAsciiString::from_unchecked(SOME_ASCII);
624            assert_ok!(sas.revalidate_soft_constraint());
625
626            let bad: SoftAsciiString =
627                SoftAsciiString::from_unchecked(SOME_NOT_ASCII);
628            assert_err!(bad.revalidate_soft_constraint());
629        }
630
631        #[test]
632        fn has_into_vec_u8() {
633            let sas = SoftAsciiString::from_unchecked("test");
634            let v: Vec<u8> = sas.into();
635            assert_eq!(v.as_slice(), b"test" as &[u8]);
636        }
637
638        #[test]
639        fn has_into_string() {
640            let sas = SoftAsciiString::from_unchecked("test");
641            let v: String = sas.into();
642            assert_eq!(v, "test");
643        }
644
645        #[test]
646        fn str_eq_string() {
647            let str = SoftAsciiStr::from_str("hy").unwrap();
648            let string = SoftAsciiString::from_string("hy").unwrap();
649
650            assert_eq!(str, str);
651            assert_eq!(str, string);
652            assert_eq!(string, str);
653            assert_eq!(string, string);
654        }
655
656        #[test]
657        fn from_str() {
658            use std::str::FromStr;
659            let s: SoftAsciiString = assert_ok!(FromStr::from_str("hy ho"));
660            assert_eq!(s, "hy ho");
661            assert_err!("↓".parse::<SoftAsciiString>());
662        }
663    }
664}