1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
// Conversion between machine integers and `char`.

use std::char;
use std::error::Error;
use std::fmt::{self, Display, Formatter};

use ::{TryFrom, TryFromIntError, Void};

impl<T> TryFrom<char> for T where T: TryFrom<u32, Err = TryFromIntError> {
    type Err = TryFromIntError;

    fn try_from (c: char) -> Result<T, TryFromIntError> {
        T::try_from(c as u32)
    }
}

#[test]
fn test_char_to_int () {
    assert_eq!(u8::try_from('~'), Ok(0x7e));
    assert_eq!(u8::try_from('\u{100}'), Err(TryFromIntError::Overflow));
}

macro_rules! impl_infallible {
    ($($ty:ty),*) => { $(
        impl TryFrom<$ty> for char {
            type Err = Void;
            fn try_from (n: $ty) -> Result<char, Void> { Ok(n as char) }
        }
    )* };
}

impl_infallible!(u8, char);

#[test]
fn test_to_char_infallible () {
    assert_eq!(char::try_from(0x7e), Ok('~'));
    assert_eq!(char::try_from('~'), Ok('~'));
}

macro_rules! impl_int_to_char {
    ($($ty:ty),*) => { $(
        impl TryFrom<$ty> for char {
            type Err = TryFromIntToCharError;

            fn try_from (n: $ty) -> Result<char, TryFromIntToCharError> {
                match u32::try_from(n)? {
                    n @ 0...0x10ffff => match char::from_u32(n) {
                        None => Err(TryFromIntToCharError::Reserved),
                        Some(c) => Ok(c),
                    },
                    _ => Err(TryFromIntToCharError::Overflow)
                }
            }
        }
    )* };
}

impl_int_to_char!(i8, i16, i32, i64, isize, u16, u32, u64, usize);

#[test]
fn test_int_to_char () {
    assert_eq!(char::try_from(-1), Err(TryFromIntToCharError::Underflow));
    assert_eq!(char::try_from(0x7eu32), Ok('~'));
    assert_eq!(char::try_from(0xd888), Err(TryFromIntToCharError::Reserved));
    assert_eq!(char::try_from(0x10ffff), Ok('\u{10ffff}'));
    assert_eq!(char::try_from(0x110000), Err(TryFromIntToCharError::Overflow));
}

/// Error which occurs when conversion from an integer to a `char` fails.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum TryFromIntToCharError {
    Overflow,
    Underflow,
    Reserved,
}

impl TryFromIntToCharError {
    fn as_str (self) -> &'static str {
        match self {
            TryFromIntToCharError::Overflow => "integer overflow",
            TryFromIntToCharError::Underflow => "integer underflow",
            TryFromIntToCharError::Reserved => "reserved code point",
        }
    }
}

impl Display for TryFromIntToCharError {
    fn fmt (&self, f: &mut Formatter) -> fmt::Result {
        f.write_str(self.as_str())
    }
}

impl Error for TryFromIntToCharError {
    fn description (&self) -> &str { self.as_str() }
}

impl From<TryFromIntError> for TryFromIntToCharError {
    fn from (other: TryFromIntError) -> TryFromIntToCharError {
        match other {
            TryFromIntError::Overflow => TryFromIntToCharError::Overflow,
            TryFromIntError::Underflow => TryFromIntToCharError::Underflow,
        }
    }
}

impl From<Void> for TryFromIntToCharError {
    fn from (_: Void) -> TryFromIntToCharError { unreachable!() }
}