use crate::types::Datatype;
use std::fmt::{self, Debug, Display, Formatter};
use std::num::ParseIntError;
use std::str::FromStr;
use thiserror::Error;
#[derive(Clone, Debug, Error, Eq, PartialEq)]
pub enum ValueError {
#[error("Value not yet known.")]
Unknown,
#[error("Expected value of type {expected} but was {actual}.")]
WrongDatatype {
expected: Datatype,
actual: Datatype,
},
#[error("Invalid or unexpected format {format}.")]
WrongFormat {
format: String,
},
#[error("Parsing {value} as datatype {datatype} failed.")]
ParseFailed {
value: String,
datatype: Datatype,
},
}
pub trait Value: ToString + FromStr {
fn datatype() -> Datatype;
fn valid_for(datatype: Option<Datatype>, format: &Option<String>) -> Result<(), ValueError> {
if let Some(actual) = datatype {
let expected = Self::datatype();
if actual != expected {
return Err(ValueError::WrongDatatype { expected, actual });
}
}
if let Some(format) = format {
Self::valid_for_format(format)
} else {
Ok(())
}
}
fn valid_for_format(_format: &str) -> Result<(), ValueError> {
Ok(())
}
}
impl Value for i64 {
fn datatype() -> Datatype {
Datatype::Integer
}
}
impl Value for f64 {
fn datatype() -> Datatype {
Datatype::Float
}
}
impl Value for bool {
fn datatype() -> Datatype {
Datatype::Boolean
}
}
impl Value for String {
fn datatype() -> Datatype {
Datatype::String
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ColorFormat {
Rgb,
Hsv,
}
impl ColorFormat {
fn as_str(&self) -> &'static str {
match self {
Self::Rgb => "rgb",
Self::Hsv => "hsv",
}
}
}
impl FromStr for ColorFormat {
type Err = ValueError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"rgb" => Ok(Self::Rgb),
"hsv" => Ok(Self::Hsv),
_ => Err(ValueError::WrongFormat {
format: s.to_owned(),
}),
}
}
}
impl Display for ColorFormat {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_str(self.as_str())
}
}
pub trait Color: Value {
fn format() -> ColorFormat;
}
impl<T: Color> Value for T {
fn datatype() -> Datatype {
Datatype::Color
}
fn valid_for_format(format: &str) -> Result<(), ValueError> {
if format == Self::format().as_str() {
Ok(())
} else {
Err(ValueError::WrongFormat {
format: format.to_owned(),
})
}
}
}
#[derive(Clone, Debug, Error, Eq, PartialEq)]
#[error("Failed to parse color.")]
pub struct ParseColorError();
impl From<ParseIntError> for ParseColorError {
fn from(_: ParseIntError) -> Self {
ParseColorError()
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ColorRgb {
pub r: u8,
pub g: u8,
pub b: u8,
}
impl ColorRgb {
pub fn new(r: u8, g: u8, b: u8) -> Self {
ColorRgb { r, g, b }
}
}
impl Display for ColorRgb {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{},{},{}", self.r, self.g, self.b)
}
}
impl FromStr for ColorRgb {
type Err = ParseColorError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let parts: Vec<_> = s.split(',').collect();
if let [r, g, b] = parts.as_slice() {
Ok(ColorRgb {
r: r.parse()?,
g: g.parse()?,
b: b.parse()?,
})
} else {
Err(ParseColorError())
}
}
}
impl Color for ColorRgb {
fn format() -> ColorFormat {
ColorFormat::Rgb
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ColorHsv {
pub h: u16,
pub s: u8,
pub v: u8,
}
impl ColorHsv {
pub fn new(h: u16, s: u8, v: u8) -> Self {
assert!(h <= 360);
assert!(s <= 100);
assert!(v <= 100);
ColorHsv { h, s, v }
}
}
impl Display for ColorHsv {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{},{},{}", self.h, self.s, self.v)
}
}
impl FromStr for ColorHsv {
type Err = ParseColorError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let parts: Vec<_> = s.split(',').collect();
if let [h, s, v] = parts.as_slice() {
let h = h.parse()?;
let s = s.parse()?;
let v = v.parse()?;
if h <= 360 && s <= 100 && v <= 100 {
return Ok(ColorHsv { h, s, v });
}
}
Err(ParseColorError())
}
}
impl Color for ColorHsv {
fn format() -> ColorFormat {
ColorFormat::Hsv
}
}
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct EnumValue(String);
impl EnumValue {
pub fn new(s: &str) -> Self {
assert!(!s.is_empty());
EnumValue(s.to_owned())
}
}
#[derive(Clone, Debug, Error, Eq, PartialEq)]
#[error("Empty string is not a valid enum value.")]
pub struct ParseEnumError();
impl FromStr for EnumValue {
type Err = ParseEnumError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.is_empty() {
Err(ParseEnumError())
} else {
Ok(EnumValue::new(s))
}
}
}
impl Display for EnumValue {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_str(&self.0)
}
}
impl Value for EnumValue {
fn datatype() -> Datatype {
Datatype::Enum
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn color_rgb_to_from_string() {
let color = ColorRgb::new(111, 222, 42);
assert_eq!(color.to_string().parse(), Ok(color));
}
#[test]
fn color_hsv_to_from_string() {
let color = ColorHsv::new(231, 88, 77);
assert_eq!(color.to_string().parse(), Ok(color));
}
#[test]
fn color_rgb_parse_invalid() {
assert_eq!("".parse::<ColorRgb>(), Err(ParseColorError()));
assert_eq!("1,2".parse::<ColorRgb>(), Err(ParseColorError()));
assert_eq!("1,2,3,4".parse::<ColorRgb>(), Err(ParseColorError()));
assert_eq!("1,2,256".parse::<ColorRgb>(), Err(ParseColorError()));
assert_eq!("1,256,3".parse::<ColorRgb>(), Err(ParseColorError()));
assert_eq!("256,2,3".parse::<ColorRgb>(), Err(ParseColorError()));
assert_eq!("1,-2,3".parse::<ColorRgb>(), Err(ParseColorError()));
}
#[test]
fn color_hsv_parse_invalid() {
assert_eq!("".parse::<ColorHsv>(), Err(ParseColorError()));
assert_eq!("1,2".parse::<ColorHsv>(), Err(ParseColorError()));
assert_eq!("1,2,3,4".parse::<ColorHsv>(), Err(ParseColorError()));
assert_eq!("1,2,101".parse::<ColorHsv>(), Err(ParseColorError()));
assert_eq!("1,101,3".parse::<ColorHsv>(), Err(ParseColorError()));
assert_eq!("361,2,3".parse::<ColorHsv>(), Err(ParseColorError()));
assert_eq!("1,-2,3".parse::<ColorHsv>(), Err(ParseColorError()));
}
}