rust_fixed_width 1.0.1

library that allows for easier manipulation of fixed width files allowing for the ability to name fields and such using specs
Documentation
extern crate pad;
use self::pad::{PadStr, Alignment};
use spec::PaddingDirection;
use std::fmt::{Display, Formatter, Error as FmtError};

#[derive(Debug)]
pub struct Error {
    repr: Box<::std::error::Error + Send + Sync>
}

impl Clone for Error {
    fn clone(&self) -> Self {
        Error::new("")
    }
}

impl Error {
    pub fn new<E>(error: E) -> Self
        where E: Into<Box<::std::error::Error + Send + Sync>>
    {
        Error { repr: error.into() }
    }

    pub fn downcast<E: ::std::error::Error + Send + Sync + 'static>(self) -> ::std::result::Result<E, Self> {
        Ok(*(self.repr.downcast::<E>().map_err(|e| Error { repr: e })?))
    }

    pub fn downcast_ref<E: ::std::error::Error + Send + Sync + 'static>(&self) -> Option<&E> {
        self.repr.downcast_ref::<E>()
    }
}

impl ::std::error::Error for Error {
    fn description(&self) -> &str {
        self.repr.description()
    }

    fn cause(&self) -> Option<&::std::error::Error> {
        self.repr.cause()
    }
}

impl Display for Error {
    fn fmt(&self, f: &mut Formatter) -> ::std::result::Result<(), FmtError> {
        Display::fmt(&*self.repr, f)
    }
}

type Result<T> = ::std::result::Result<T, Error>;

pub trait Padder {
    fn pad(&self, data: String, length: usize, padding: &String, direction: PaddingDirection) -> Result<String>;
}

impl<'a, T> Padder for &'a T where T: 'a + Padder {
    fn pad(&self, data: String, length: usize, padding: &String, direction: PaddingDirection) -> Result<String> {
        (**self).pad(data, length, padding, direction)
    }
}

pub trait UnPadder {
    fn unpad(&self, data: String, padding: &String, direction: PaddingDirection) -> Result<String>;
}

impl<'a, T> UnPadder for &'a T where T: 'a + UnPadder {
    fn unpad(&self, data: String, padding: &String, direction: PaddingDirection) -> Result<String> {
        (**self).unpad(data, padding, direction)
    }
}

pub struct DefaultPadder;

#[derive(Debug)]
pub enum PaddingError {
    PaddingLongerThanOne(usize)
}

impl ::std::error::Error for PaddingError {
    fn description(&self) -> &str {
        match *self {
            PaddingError::PaddingLongerThanOne(_) => "The padding string must be only one char long to use this padder"
        }
    }
}

impl Display for PaddingError {
    fn fmt(&self, f: &mut Formatter) -> ::std::result::Result<(), FmtError> {
        match *self {
            PaddingError::PaddingLongerThanOne(len) => write!(
                f,
                "the padding string was {} chars long it can only be at most 1 char long",
                len
            )
        }
    }
}

impl From<PaddingError> for Error {
    fn from(e: PaddingError) -> Self {
        Error::new(e)
    }
}

impl DefaultPadder {
    fn get_char(padding: &String) -> ::std::result::Result<char, PaddingError> {
        if padding.len() > 1 {
            Err(PaddingError::PaddingLongerThanOne(padding.len()))
        } else {
            Ok(padding.chars().next().or(Some(' ')).expect("should have a some no matter what"))
        }
    }
}

impl Padder for DefaultPadder {
    fn pad(&self, data: String, length: usize, padding: &String, direction: PaddingDirection) -> Result<String> {
        Ok(data.pad(
            length,
            Self::get_char(padding)?,
            match direction {
                PaddingDirection::Left => Alignment::Right,
                PaddingDirection::Right => Alignment::Left,
            },
            false
        ))
    }
}

impl UnPadder for DefaultPadder {
    fn unpad(&self, data: String, padding: &String, direction: PaddingDirection) -> Result<String> {
        Ok(match direction {
            PaddingDirection::Left => data.trim_left_matches(Self::get_char(padding)?).to_string(),
            PaddingDirection::Right => data.trim_right_matches(Self::get_char(padding)?).to_string(),
        })
    }
}

pub struct IdentityPadder;

impl Padder for IdentityPadder {
    fn pad(&self, data: String, _: usize, _: &String, _: PaddingDirection) -> Result<String> {
        Ok(data)
    }
}

impl UnPadder for IdentityPadder {
    fn unpad(&self, data: String, _: &String, _: PaddingDirection) -> Result<String> {
        Ok(data)
    }
}


#[cfg(test)]
mod test {
    use super::*;
    use spec::*;

    #[test]
    fn default_padder() {
        let padder = DefaultPadder;
        let data = "qwer".to_string();
        assert_result!(Ok("qwer333333".to_string()), padder.pad(data.clone(), 10, &"3".to_string(), PaddingDirection::Right));
        let data = "qwer".to_string();
        assert_result!(Ok("333333qwer".to_string()), padder.pad(data.clone(), 10, &"3".to_string(), PaddingDirection::Left));
        assert_result!(
            Err(PaddingError::PaddingLongerThanOne(2)),
            padder.pad(data.clone(), 10, &"33".to_string(), PaddingDirection::Left).map_err(|e| e.downcast::<PaddingError>().unwrap())
        );
        let data = "qwer333333".to_string();
        assert_result!(Ok("qwer".to_string()), padder.unpad(data.clone(), &"3".to_string(), PaddingDirection::Right));
        let data = "333333qwer".to_string();
        assert_result!(Ok("qwer".to_string()), padder.unpad(data.clone(), &"3".to_string(), PaddingDirection::Left));
        assert_result!(
            Err(PaddingError::PaddingLongerThanOne(2)),
            padder.unpad(data.clone(), &"33".to_string(), PaddingDirection::Left).map_err(|e| e.downcast::<PaddingError>().unwrap())
        );
    }

    #[test]
    fn identity_padder() {
        let padder = IdentityPadder;
        let data = "qwer".to_string();
        assert_result!(Ok(data.clone()), padder.pad(data.clone(), 10, &"3".to_string(), PaddingDirection::Right));
        assert_result!(Ok(data.clone()), padder.pad(data.clone(), 10, &"3".to_string(), PaddingDirection::Left));
        assert_result!(Ok(data.clone()), padder.unpad(data.clone(), &"3".to_string(), PaddingDirection::Right));
        assert_result!(Ok(data.clone()), padder.unpad(data.clone(), &"3".to_string(), PaddingDirection::Left));
    }

    #[test]
    fn padder_reference() {
        let padder = IdentityPadder;
        let data = "qwer".to_string();
        assert_result!(Ok(data.clone()), Padder::pad(&&padder, data.clone(), 10, &"3".to_string(), PaddingDirection::Right));
        assert_result!(Ok(data.clone()), UnPadder::unpad(&&padder, data.clone(), &"3".to_string(), PaddingDirection::Right));
    }

    #[test]
    fn error() {
        let error = Error::new(PaddingError::PaddingLongerThanOne(23));
        assert_option!(Some(&PaddingError::PaddingLongerThanOne(23)), error.downcast_ref::<PaddingError>());
        assert_option!(Some(&PaddingError::PaddingLongerThanOne(23)), error.downcast_ref::<PaddingError>());
        match error.downcast::<PaddingError>() {
            Ok(PaddingError::PaddingLongerThanOne(23)) => (),
            e => panic!("bad result returned {:?}", e)
        }
    }
}