Skip to content

Commit bfbcb06

Browse files
committed
Add trait IntoAscii to replace AsciiCast<Target=Ascii>
`AsciiCast` requires an explicit lifetime, which doesn't make sense for `Target=Ascii`: `fn by_value<'a,C:AsciiCast<'a,Target=Ascii>>(ch: C) {…}` doesn't compile (`'a` doesn't appear anywhere) so you have to borrow ch: `fn by_ref<'a,C:AsciiCast<'a,Target=Ascii>>(c: &'a C) {…}`. But then you have to explicitly borrow it in the caller: `by_ref(& 'a');` which looks bad. Names are also more in line with what std uses: xxx_unchecked() and Into/From.
1 parent e39560a commit bfbcb06

File tree

2 files changed

+83
-9
lines changed

2 files changed

+83
-9
lines changed

src/ascii.rs

Lines changed: 82 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::mem::transmute;
22
use std::fmt;
3-
#[cfg(feature="unstable")]
3+
use std::error::Error;
44
use std::ascii::AsciiExt;
55

66
use AsciiCast;
@@ -279,7 +279,7 @@ pub enum Ascii {
279279
}
280280

281281
impl Ascii {
282-
/// Constructs an Ascii character from a `char`.
282+
/// Constructs an Ascii character from a `char` or `u8`.
283283
///
284284
/// # Failure
285285
///
@@ -292,11 +292,13 @@ impl Ascii {
292292
/// assert_eq!(a.as_char(), 'g');
293293
/// ```
294294
#[inline]
295-
pub fn from(ch: char) -> Result<Ascii, ()> {
296-
unsafe{if ch as u32 <= 0x7F {
297-
return Ok(ch.to_ascii_nocheck());
298-
}}
299-
Err(())
295+
pub fn from<C:IntoAscii>(ch: C) -> Result<Self, ()> {
296+
ch.into_ascii().map_err(|_| () )
297+
}
298+
299+
/// Constructs an Ascii character from a `char` or `u8` without any checks.
300+
pub unsafe fn from_unchecked<C:IntoAscii>(ch: C) -> Self {
301+
ch.into_ascii_unchecked()
300302
}
301303

302304
/// Constructs an Ascii character from a `u8`.
@@ -530,10 +532,73 @@ impl<'a> AsciiCast<'a> for char {
530532
}
531533
}
532534

535+
536+
537+
const ERRORMSG_CHAR: &'static str = "not an ASCII character";
538+
/// Error returned by `IntoAscii.into_ascii()` when the parameter is greater than 127.
539+
#[derive(PartialEq)]
540+
pub struct IntoAsciiError(());
541+
impl fmt::Debug for IntoAsciiError {
542+
fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result {
543+
write!(fmtr, "{}", ERRORMSG_CHAR)
544+
}
545+
}
546+
impl fmt::Display for IntoAsciiError {
547+
fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result {
548+
write!(fmtr, "{}", ERRORMSG_CHAR)
549+
}
550+
}
551+
impl Error for IntoAsciiError {
552+
fn description(&self) -> &'static str {
553+
ERRORMSG_CHAR
554+
}
555+
}
556+
557+
558+
/// Trait for converting `char` and `u8` into `Ascii`.
559+
pub trait IntoAscii : AsciiExt {
560+
/// Convert to `Ascii` without checking if it's less than 128.
561+
unsafe fn into_ascii_unchecked(self) -> Ascii;
562+
/// Convert to `Ascii`.
563+
fn into_ascii(self) -> Result<Ascii,IntoAsciiError>;
564+
}
565+
566+
#[cfg(feature = "unstable")]
567+
impl IntoAscii for Ascii {
568+
fn into_ascii(self) -> Result<Ascii,IntoAsciiError> {
569+
Ok(self)
570+
}
571+
unsafe fn into_ascii_unchecked(self) -> Ascii {
572+
self
573+
}
574+
}
575+
impl IntoAscii for u8 {
576+
fn into_ascii(self) -> Result<Ascii,IntoAsciiError> {
577+
unsafe{if self <= 0x7F {
578+
return Ok(self.into_ascii_unchecked());
579+
}}
580+
Err(IntoAsciiError(()))
581+
}
582+
unsafe fn into_ascii_unchecked(self) -> Ascii {
583+
transmute(self)
584+
}
585+
}
586+
impl IntoAscii for char {
587+
fn into_ascii(self) -> Result<Ascii,IntoAsciiError> {
588+
unsafe{if self as u32 <= 0x7F {
589+
return Ok(self.into_ascii_unchecked());
590+
}}
591+
Err(IntoAsciiError(()))
592+
}
593+
unsafe fn into_ascii_unchecked(self) -> Ascii {
594+
(self as u8).into_ascii_unchecked()
595+
}
596+
}
597+
533598
#[cfg(test)]
534599
mod tests {
535600
use AsciiCast;
536-
use super::Ascii;
601+
use super::{Ascii,IntoAscii,IntoAsciiError};
537602

538603
#[test]
539604
fn to_ascii() {
@@ -544,6 +609,15 @@ mod tests {
544609
assert_eq!('λ'.to_ascii(), Err(()));
545610
}
546611

612+
#[test]
613+
fn into_ascii() {
614+
fn generic<C:IntoAscii>(c: C) -> Result<Ascii,IntoAsciiError> {
615+
c.into_ascii()
616+
}
617+
assert_eq!(generic('A'), Ok(Ascii::A));
618+
assert_eq!(generic(b'A'), Ok(Ascii::A));
619+
}
620+
547621
#[test]
548622
fn as_byte() {
549623
assert_eq!(65u8.to_ascii().unwrap().as_byte(), 65u8);

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ mod ascii_str;
2121
use std::borrow::Borrow;
2222
use std::ascii::AsciiExt;
2323

24-
pub use ascii::Ascii;
24+
pub use ascii::{Ascii, IntoAscii, IntoAsciiError};
2525
pub use ascii_string::AsciiString;
2626
pub use ascii_str::AsciiStr;
2727

0 commit comments

Comments
 (0)