rust-version 0.1.0

Crate for parsing Rust versions.
Documentation
use std::fmt;

use itoa;

/// Internal trait; parseable, unsigned integers.
pub trait Parseable: Sized + From<u8> + fmt::Debug {
    fn checked_mul_ten(self) -> Option<Self>;
    fn checked_add(self, digit: Self) -> Option<Self>;
    fn write_to(self, bytes: &mut [u8]) -> usize;
}
macro_rules! impl_parseable {
    ($($t:ident)*) => {
        $(
            impl Parseable for $t {
                fn checked_mul_ten(self) -> Option<Self> {
                    self.checked_mul(10)
                }
                fn checked_add(self, rhs: Self) -> Option<Self> {
                    self.checked_add(rhs)
                }
                fn write_to(self, bytes: &mut [u8]) -> usize {
                    itoa::write(bytes, self).unwrap()
                }
            }
        )*
    }
}
impl_parseable! { u8 u16 u32 u64 }

/// Error encountered when parsing an unsigned integer.
pub enum ParseUintError {
    /// The byte slice was empty.
    Empty,

    /// One of the given bytes was not valid under the given radix.
    BadByte(u8),

    /// The number was too big to be represented.
    Overflow,
}

/// Parses a byte slice into an unsigned integer.
pub fn parse_uint<T: Parseable>(bytes: &[u8]) -> Result<T, ParseUintError> {
    let mut iter = bytes.iter().map(|&d| {
        (d as char)
            .to_digit(10)
            .map(|d| T::from(d as u8))
            .ok_or_else(|| ParseUintError::BadByte(d))
    });

    match iter.next() {
        Some(digit) => {
            iter.fold(digit, |curr, digit| {
                digit.and_then(|digit| {
                    curr.and_then(|curr| {
                        curr.checked_mul_ten()
                            .and_then(|curr| curr.checked_add(digit))
                            .ok_or(ParseUintError::Overflow)
                    })
                })
            })
        }
        None => Err(ParseUintError::Empty),
    }
}