use std::str::FromStr;
use std::num::ParseIntError;
use regex::Regex;
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Dimension {
Cells(u32),
Pixels(u32),
Percent(u32),
Auto
}
#[derive(Debug, Fail, PartialEq)]
pub enum DimensionParseError {
#[fail(display = "Invalid dimension format")]
InvalidFormat,
#[fail(display = "Invalid integer")]
ParseInt(ParseIntError),
#[fail(display = "Invalid unit")]
InvalidUnit,
}
impl From<ParseIntError> for DimensionParseError {
fn from(error: ParseIntError) -> DimensionParseError {
DimensionParseError::ParseInt(error)
}
}
impl FromStr for Dimension {
type Err = DimensionParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
lazy_static! {
static ref RE: Regex = Regex::new(r"^(\d+)(.+)?$").unwrap();
}
if s == "auto" {
Ok(Dimension::Auto)
} else {
let caps = RE.captures(s).ok_or(DimensionParseError::InvalidFormat)?;
let n = caps[1].parse::<u32>()?;
match caps.get(2).map(|m| m.as_str()) {
None => Ok(Dimension::Cells(n)),
Some("px") => Ok(Dimension::Pixels(n)),
Some("%") => Ok(Dimension::Percent(n)),
Some(_) => Err(DimensionParseError::InvalidUnit),
}
}
}
}
impl Into<String> for Dimension {
fn into(self) -> String {
match self {
Dimension::Cells(cells) => format!("{}", cells),
Dimension::Pixels(pixels) => format!("{}px", pixels),
Dimension::Percent(percent) => format!("{}%", percent),
Dimension::Auto => "auto".to_string()
}
}
}
mod tests {
#![allow(dead_code)]
#![allow(unused_imports)]
use super::*;
fn test_to_string(d: Dimension, s: &str){
assert_eq!(<Dimension as Into<String>>::into(d), s);
}
fn test_parse(s: &str, r: Result<Dimension, DimensionParseError>){
assert_eq!(s.parse::<Dimension>(), r);
}
mod parse_dimension {
use super::{test_parse, super::{Dimension, DimensionParseError}};
#[test]
fn cells_valid() {
test_parse("1", Ok(Dimension::Cells(1)));
test_parse("42", Ok(Dimension::Cells(42)));
}
#[test]
fn pixels_valid() {
test_parse("1px", Ok(Dimension::Pixels(1)));
test_parse("600px", Ok(Dimension::Pixels(600)));
}
#[test]
fn percent_valid() {
test_parse("1%", Ok(Dimension::Percent(1)));
test_parse("99%", Ok(Dimension::Percent(99)));
}
#[test]
fn auto_valid() {
test_parse("auto", Ok(Dimension::Auto));
}
#[test]
fn bad_unit_invalid_unit() {
test_parse("20invalids", Err(DimensionParseError::InvalidUnit));
test_parse("42towels", Err(DimensionParseError::InvalidUnit));
test_parse("300 px", Err(DimensionParseError::InvalidUnit));
}
#[test]
fn bad_format_invalid_format() {
test_parse("This is bad", Err(DimensionParseError::InvalidFormat));
test_parse("px300", Err(DimensionParseError::InvalidFormat));
}
}
mod dimension_to_string {
use super::{test_to_string, super::Dimension};
#[test]
fn auto_valid(){
test_to_string(Dimension::Auto, "auto");
}
#[test]
fn cells_valid(){
test_to_string(Dimension::Cells(1), "1");
test_to_string(Dimension::Cells(42), "42");
}
#[test]
fn pixels_valid(){
test_to_string(Dimension::Pixels(2), "2px");
test_to_string(Dimension::Pixels(75), "75px");
}
#[test]
fn percent_valid(){
test_to_string(Dimension::Percent(1), "1%");
test_to_string(Dimension::Percent(98), "98%");
}
}
}