Skip to content

Commit 23bec58

Browse files
author
Thomas Bahn
authored
Merge pull request #22 from tormol/FromAsciiError
Add `FromAsciiError` error type
2 parents 216d5bb + 289b8d9 commit 23bec58

File tree

4 files changed

+85
-41
lines changed

4 files changed

+85
-41
lines changed

RELEASES.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
1-
Version 0.7.0 (2016-06-25)
1+
Unreleased
22
==========
3-
* Rename `Ascii` to `AsciiChar` and convert it into an enum. (with a variant for every ASCII character)
3+
* Return `FromAsciiError` instead of the input when `AsciiString::from_ascii()` or `into_ascii_string()` fails.
4+
5+
Version 0.7.0 (2016-06-25)
6+
==========================
7+
* Rename `Ascii` to `AsciiChar` and convert it into an enum.
8+
(with a variant for every ASCII character)
49
* Replace `OwnedAsciiCast` with `IntoAsciiString`.
510
* Replace `AsciiCast` with `As[Mut]AsciiStr` and `IntoAsciiChar`.
611
* Add *from[_ascii]_unchecked* methods.
712
* Replace *from_bytes* with *from_ascii* in method names.
8-
* Return `std::error::Error`-implementing types on error instead of `Option::None` or `Err(())`.
13+
* Return `std::error::Error`-implementing types instead of `()` and `None` when
14+
conversion to `AsciiStr` or `AsciiChar` fails.
915
* Implement `AsciiExt` without the `unstable` Cargo feature flag, which is removed.
1016
* Require Rust 1.9 or later.
1117
* Add `#[no_std]` support in a Cargo feature.

src/ascii_string.rs

Lines changed: 68 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::{fmt, mem};
2-
use std::ascii::AsciiExt;
32
use std::borrow::Borrow;
3+
use std::error::Error;
4+
use std::any::Any;
45
use std::str::FromStr;
56
use std::ops::{Deref, DerefMut, Add, Index, IndexMut};
67
use std::iter::FromIterator;
@@ -93,15 +94,7 @@ impl AsciiString {
9394
pub unsafe fn from_ascii_unchecked<B>(bytes: B) -> Self
9495
where B: Into<Vec<u8>>
9596
{
96-
let bytes: Vec<u8> = bytes.into();
97-
let vec = Vec::from_raw_parts(bytes.as_ptr() as *mut AsciiChar,
98-
bytes.len(),
99-
bytes.capacity());
100-
101-
// We forget `src` to avoid freeing it at the end of the scope.
102-
// Otherwise, the returned `AsciiString` would point to freed memory.
103-
mem::forget(bytes);
104-
AsciiString { vec: vec }
97+
AsciiString { vec: mem::transmute(bytes.into()) }
10598
}
10699

107100
/// Converts anything that can represent a byte buffer into an `AsciiString`.
@@ -112,21 +105,17 @@ impl AsciiString {
112105
/// # Examples
113106
/// ```
114107
/// # use ascii::AsciiString;
115-
/// let foo = AsciiString::from_ascii("foo").unwrap();
116-
/// let err = AsciiString::from_ascii("Ŋ");
108+
/// let foo = AsciiString::from_ascii("foo".to_string()).unwrap();
109+
/// let err = AsciiString::from_ascii("Ŋ".to_string()).unwrap_err();
117110
/// assert_eq!(foo.as_str(), "foo");
118-
/// assert_eq!(err, Err("Ŋ"));
111+
/// assert_eq!(err.into_source(), "Ŋ");
119112
/// ```
120-
pub fn from_ascii<B>(bytes: B) -> Result<AsciiString, B>
121-
where B: Into<Vec<u8>> + AsRef<[u8]>
122-
{
123-
unsafe {
124-
if bytes.as_ref().is_ascii() {
125-
Ok( AsciiString::from_ascii_unchecked(bytes) )
126-
} else {
127-
Err(bytes)
128-
}
129-
}
113+
pub fn from_ascii<B: Into<Vec<u8>> + AsRef<[u8]>>
114+
(bytes: B) -> Result<AsciiString, FromAsciiError<B>> {
115+
unsafe{ match bytes.as_ref().as_ascii_str() {
116+
Ok(_) => Ok(AsciiString::from_ascii_unchecked(bytes)),
117+
Err(e) => Err(FromAsciiError{error: e, owner: bytes}),
118+
}}
130119
}
131120

132121
/// Pushes the given ASCII string onto this ASCII string buffer.
@@ -536,19 +525,69 @@ impl<T> IndexMut<T> for AsciiString where AsciiStr: IndexMut<T> {
536525
}
537526

538527

528+
/// A possible error value when converting an `AsciiString` from a byte vector or string.
529+
/// It wraps an `AsAsciiStrError` which you can get through the `ascii_error()` method.
530+
///
531+
/// This is the error type for `AsciiString::from_ascii()` and `IntoAsciiString::into_ascii_string()``
532+
/// They will never clone or touch the content of the original type;
533+
/// It can be extracted by the `into_source` method.
534+
///
535+
/// #Examples
536+
/// ```
537+
/// # use ascii::IntoAsciiString;
538+
/// let err = "bø!".to_string().into_ascii_string().unwrap_err();
539+
/// assert_eq!(err.ascii_error().valid_up_to(), 1);
540+
/// assert_eq!(err.into_source(), "bø!".to_string());
541+
/// ```
542+
#[derive(Clone,Copy, PartialEq,Eq)]
543+
pub struct FromAsciiError<O> {
544+
error: AsAsciiStrError,
545+
owner: O,
546+
}
547+
impl<O> FromAsciiError<O> {
548+
/// Get the position of the first non-ASCII byte or character.
549+
pub fn ascii_error(&self) -> AsAsciiStrError {
550+
self.error
551+
}
552+
/// Get back the original, unmodified type.
553+
pub fn into_source(self) -> O {
554+
self.owner
555+
}
556+
}
557+
impl<O> fmt::Debug for FromAsciiError<O> {
558+
fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result {
559+
fmt::Debug::fmt(&self.error, fmtr)
560+
}
561+
}
562+
impl<O> fmt::Display for FromAsciiError<O> {
563+
fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result {
564+
fmt::Display::fmt(&self.error, fmtr)
565+
}
566+
}
567+
impl<O:Any> Error for FromAsciiError<O> {
568+
fn description(&self) -> &str {
569+
self.error.description()
570+
}
571+
/// Always returns an `AsAsciiStrError`
572+
fn cause(&self) -> Option<&Error> {
573+
Some(&self.error as &Error)
574+
}
575+
}
576+
577+
539578
/// Convert vectors into `AsciiString`.
540579
pub trait IntoAsciiString : Sized {
541580
/// Convert to `AsciiString` without checking for non-ASCII characters.
542581
unsafe fn into_ascii_string_unchecked(self) -> AsciiString;
543582
/// Convert to `AsciiString`.
544-
fn into_ascii_string(self) -> Result<AsciiString,Self>;
583+
fn into_ascii_string(self) -> Result<AsciiString, FromAsciiError<Self>>;
545584
}
546585

547586
impl IntoAsciiString for AsciiString {
548587
unsafe fn into_ascii_string_unchecked(self) -> AsciiString {
549588
self
550589
}
551-
fn into_ascii_string(self) -> Result<AsciiString,Self> {
590+
fn into_ascii_string(self) -> Result<Self, FromAsciiError<Self>> {
552591
Ok(self)
553592
}
554593
}
@@ -557,7 +596,7 @@ impl IntoAsciiString for Vec<AsciiChar> {
557596
unsafe fn into_ascii_string_unchecked(self) -> AsciiString {
558597
AsciiString::from(self)
559598
}
560-
fn into_ascii_string(self) -> Result<AsciiString,Self> {
599+
fn into_ascii_string(self) -> Result<AsciiString, FromAsciiError<Self>> {
561600
Ok(AsciiString::from(self))
562601
}
563602
}
@@ -566,7 +605,7 @@ impl IntoAsciiString for Vec<u8> {
566605
unsafe fn into_ascii_string_unchecked(self) -> AsciiString {
567606
AsciiString::from_ascii_unchecked(self)
568607
}
569-
fn into_ascii_string(self) -> Result<AsciiString,Self> {
608+
fn into_ascii_string(self) -> Result<AsciiString, FromAsciiError<Self>> {
570609
AsciiString::from_ascii(self)
571610
}
572611
}
@@ -575,7 +614,7 @@ impl IntoAsciiString for String {
575614
unsafe fn into_ascii_string_unchecked(self) -> AsciiString {
576615
self.into_bytes().into_ascii_string_unchecked()
577616
}
578-
fn into_ascii_string(self) -> Result<AsciiString,Self> {
617+
fn into_ascii_string(self) -> Result<AsciiString, FromAsciiError<Self>> {
579618
AsciiString::from_ascii(self)
580619
}
581620
}
@@ -606,14 +645,9 @@ mod tests {
606645
}
607646

608647
#[test]
609-
fn fmt_display_ascii_string() {
648+
fn fmt_ascii_string() {
610649
let s = "abc".to_string().into_ascii_string().unwrap();
611650
assert_eq!(format!("{}", s), "abc".to_string());
612-
}
613-
614-
#[test]
615-
fn fmt_debug_ascii_string() {
616-
let s = "abc".to_string().into_ascii_string().unwrap();
617651
assert_eq!(format!("{:?}", s), "\"abc\"".to_string());
618652
}
619653
}

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,4 @@ mod ascii_string;
3939
pub use ascii_char::{AsciiChar, ToAsciiChar, ToAsciiCharError};
4040
pub use ascii_str::{AsciiStr, AsAsciiStr, AsMutAsciiStr, AsAsciiStrError};
4141
#[cfg(not(feature = "no_std"))]
42-
pub use ascii_string::{AsciiString, IntoAsciiString};
42+
pub use ascii_string::{AsciiString, IntoAsciiString, FromAsciiError};

tests.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,17 @@ fn to_ascii() {
3030
#[test]
3131
#[cfg(not(feature = "no_std"))]
3232
fn into_ascii() {
33-
assert_eq!("zoä华".to_string().into_ascii_string(), Err("zoä华".to_string()));
34-
assert_eq!(vec![127_u8, 128, 255].into_ascii_string(), Err(vec![127_u8, 128, 255]));
35-
3633
let arr = [AsciiChar::ParenOpen, AsciiChar::Space, AsciiChar::Semicolon];
3734
let v = AsciiString::from(arr.to_vec());
3835
assert_eq!(b"( ;".to_vec().into_ascii_string(), Ok(v.clone()));
3936
assert_eq!("( ;".to_string().into_ascii_string(), Ok(v));
37+
38+
let err = "zoä华".to_string().into_ascii_string().unwrap_err();
39+
assert_eq!(Err(err.ascii_error()), "zoä华".as_ascii_str());
40+
assert_eq!(err.into_source(), "zoä华");
41+
let err = vec![127, 128, 255].into_ascii_string().unwrap_err();
42+
assert_eq!(Err(err.ascii_error()), [127, 128, 255].as_ascii_str());
43+
assert_eq!(err.into_source(), &[127, 128, 255]);
4044
}
4145

4246
#[test]

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy