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#[allow(warnings)]
21use std::ascii::AsciiExt;
22
23use error::{StringFromStrError, FromSourceError};
24use soft_char::SoftAsciiChar;
25use soft_str::SoftAsciiStr;
26
27#[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 #[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 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}