diff options
Diffstat (limited to 'src/values')
| -rw-r--r-- | src/values/alignment.rs | 65 | ||||
| -rw-r--r-- | src/values/content_fit.rs | 69 | ||||
| -rw-r--r-- | src/values/length.rs | 145 | ||||
| -rw-r--r-- | src/values/line_height.rs | 117 | ||||
| -rw-r--r-- | src/values/padding.rs | 201 | ||||
| -rw-r--r-- | src/values/pixels.rs | 18 | ||||
| -rwxr-xr-x | src/values/rotation.rs | 114 |
7 files changed, 729 insertions, 0 deletions
diff --git a/src/values/alignment.rs b/src/values/alignment.rs new file mode 100644 index 0000000..7e9fcab --- /dev/null +++ b/src/values/alignment.rs @@ -0,0 +1,65 @@ +use iced::Alignment; + +use super::Value; + +#[derive(Debug, thiserror::Error, Clone, PartialEq)] +pub enum ParseAlignmentError { + #[error("cannot parse rotation from empty string")] + Empty, + #[error("invalid variant")] + InvalidVariant, +} + +impl Value for Alignment { + type Err = ParseAlignmentError; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + let s = s.trim(); + + if s.is_empty() { + return Err(ParseAlignmentError::Empty); + } + + match s { + "start" => Ok(Self::Start), + "center" => Ok(Self::Center), + "end" => Ok(Self::End), + _ => Err(ParseAlignmentError::InvalidVariant), + } + } + + fn to_string(&self) -> String { + match self { + Self::Start => String::from("start"), + Self::Center => String::from("center"), + Self::End => String::from("end"), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_parse_with_spaces() { + assert_eq!(Alignment::from_str(" start"), Ok(Alignment::Start)); + + assert_eq!(Alignment::from_str(" center "), Ok(Alignment::Center)); + + assert_eq!(Alignment::from_str("end "), Ok(Alignment::End)) + } + + #[test] + fn cant_parse_invalid_variant() { + assert_eq!( + Alignment::from_str("middle"), + Err(ParseAlignmentError::InvalidVariant) + ) + } + + #[test] + fn cant_parse_empty_string() { + assert_eq!(Alignment::from_str(" "), Err(ParseAlignmentError::Empty)) + } +} diff --git a/src/values/content_fit.rs b/src/values/content_fit.rs new file mode 100644 index 0000000..053431f --- /dev/null +++ b/src/values/content_fit.rs @@ -0,0 +1,69 @@ +use iced::ContentFit; + +use super::Value; + +#[derive(Debug, thiserror::Error, Clone, PartialEq)] +pub enum ParseContentFitError { + #[error("invalid variant")] + InvalidVariant, +} + +impl Value for ContentFit { + type Err = ParseContentFitError; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + let s = s.trim(); + + if s.is_empty() { + Ok(Self::default()) + } else { + match s { + "fill" => Ok(Self::Fill), + "none" => Ok(Self::None), + "cover" => Ok(Self::Cover), + "contain" => Ok(Self::Contain), + "scale_down" => Ok(Self::ScaleDown), + _ => Err(ParseContentFitError::InvalidVariant), + } + } + } + + fn to_string(&self) -> String { + match self { + Self::Fill => String::from("fill"), + Self::None => String::from("none"), + Self::Cover => String::from("cover"), + Self::Contain => String::from("contain"), + Self::ScaleDown => String::from("scale_down"), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_parse_with_spaces() { + assert_eq!(ContentFit::from_str(" fill"), Ok(ContentFit::Fill)); + + assert_eq!(ContentFit::from_str(" none "), Ok(ContentFit::None)); + + assert_eq!(ContentFit::from_str("cover "), Ok(ContentFit::Cover)); + + assert_eq!(ContentFit::from_str("contain"), Ok(ContentFit::Contain)); + + assert_eq!( + ContentFit::from_str("scale_down"), + Ok(ContentFit::ScaleDown) + ) + } + + #[test] + fn cant_parse_invalid_variant() { + assert_eq!( + ContentFit::from_str("clip"), + Err(ParseContentFitError::InvalidVariant) + ) + } +} diff --git a/src/values/length.rs b/src/values/length.rs new file mode 100644 index 0000000..556f8ff --- /dev/null +++ b/src/values/length.rs @@ -0,0 +1,145 @@ +use std::num::{ParseFloatError, ParseIntError}; +use std::str::FromStr; + +use iced::Length; + +use super::Value; + +#[derive(Debug, thiserror::Error, Clone, PartialEq)] +pub enum ParseLengthError { + #[error("float parsing error: {0}")] + ParseFloatError(ParseFloatError), + #[error("int parsing error: {0}")] + ParseIntError(ParseIntError), + #[error("invalid type")] + InvalidType, + #[error("invalid prefix")] + InvalidPrefix, + #[error("missing prefix")] + MissingPrefix, + #[error("cannot parse length from empty string")] + Empty, +} + +impl From<ParseFloatError> for ParseLengthError { + fn from(value: ParseFloatError) -> Self { + Self::ParseFloatError(value) + } +} + +impl From<ParseIntError> for ParseLengthError { + fn from(value: ParseIntError) -> Self { + Self::ParseIntError(value) + } +} + +impl Value for Length { + type Err = ParseLengthError; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + let s = s.trim(); + + if s.is_empty() { + return Err(ParseLengthError::Empty); + } + + if !s.contains(|c: char| c.is_ascii_digit()) { + match s { + "fill" => Ok(Self::Fill), + "shrink" => Ok(Self::Shrink), + _ => Err(ParseLengthError::InvalidType), + } + } else { + if s.starts_with(|c: char| c.is_ascii_digit()) { + return Err(ParseLengthError::MissingPrefix); + } + + let (prefix, value) = s.split_at(2); + match prefix.to_lowercase().as_str() { + "fx" => Ok(Self::Fixed(f32::from_str(value)?)), + "fp" => Ok(Self::FillPortion(u16::from_str(value)?)), + _ => Err(ParseLengthError::InvalidPrefix), + } + } + } + + fn to_string(&self) -> String { + match self { + Self::Fill => String::from("fill"), + Self::Shrink => String::from("shrink"), + Self::Fixed(value) => format!("fx{}", value), + Self::FillPortion(value) => format!("fp{}", value), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_parse_fill() { + assert_eq!(Length::from_str("fill"), Ok(Length::Fill)) + } + + #[test] + fn can_parse_shrink_with_space() { + assert_eq!(Length::from_str("shrink "), Ok(Length::Shrink)) + } + + #[test] + fn can_parse_fill_portion() { + assert_eq!(Length::from_str("fp15"), Ok(Length::FillPortion(15))) + } + + #[test] + fn can_parse_fixed_with_spaces() { + assert_eq!(Length::from_str(" fx3.1 "), Ok(Length::Fixed(3.1))) + } + + #[test] + fn cant_parse_invalid_type() { + assert_eq!( + Length::from_str("fillportion"), + Err(ParseLengthError::InvalidType) + ) + } + + #[test] + fn cant_parse_invalid_prefix() { + assert_eq!( + Length::from_str("f2.0"), + Err(ParseLengthError::InvalidPrefix), + ) + } + + #[test] + fn cant_parse_invalid_float() { + assert_eq!( + Length::from_str(" fx2.a"), + Err(ParseLengthError::ParseFloatError( + f32::from_str("2.a").expect_err("float parse should fail") + )) + ) + } + + #[test] + fn cant_parse_invalid_integer() { + assert_eq!( + Length::from_str("fp1a "), + Err(ParseLengthError::ParseIntError( + u16::from_str("1a").expect_err("integer parse should fail") + )) + ) + } + + #[test] + fn cant_parse_with_missing_prefix() { + assert_eq!(Length::from_str("24"), Err(ParseLengthError::MissingPrefix)) + } + + #[test] + fn cant_parse_empty_string() { + assert_eq!(Length::from_str(" "), Err(ParseLengthError::Empty)) + } +} diff --git a/src/values/line_height.rs b/src/values/line_height.rs new file mode 100644 index 0000000..0ea1524 --- /dev/null +++ b/src/values/line_height.rs @@ -0,0 +1,117 @@ +use std::num::ParseFloatError; +use std::str::FromStr; + +use iced::Pixels; +use iced::advanced::text::LineHeight; + +use super::Value; + +#[derive(Debug, thiserror::Error, Clone, PartialEq)] +pub enum ParseLineHeightError { + #[error("float parsing error: {0}")] + ParseFloatError(ParseFloatError), + #[error("missing prefix")] + MissingPrefix, + #[error("invalid prefix")] + InvalidPrefix, + #[error("cannot parse line height from empty string")] + Empty, +} + +impl From<ParseFloatError> for ParseLineHeightError { + fn from(value: ParseFloatError) -> Self { + Self::ParseFloatError(value) + } +} + +impl Value for LineHeight { + type Err = ParseLineHeightError; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + let s = s.trim(); + + if s.is_empty() { + return Err(ParseLineHeightError::Empty); + } + + if s.starts_with(|c: char| !c.is_ascii_digit()) { + let (prefix, value) = s.split_at(1); + match prefix.to_lowercase().as_str() { + "r" => Ok(Self::Relative(f32::from_str(value)?)), + "a" => Ok(Self::Absolute(Pixels::from_str(value)?)), + _ => Err(ParseLineHeightError::InvalidPrefix), + } + } else { + Err(ParseLineHeightError::MissingPrefix) + } + } + + fn to_string(&self) -> String { + match self { + Self::Relative(value) => format!("r{}", value), + Self::Absolute(value) => format!("a{}", value.0), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_parse_with_r_prefix() { + assert_eq!( + LineHeight::from_str(" r3.2"), + Ok(LineHeight::Relative(3.2)) + ); + + assert_eq!( + LineHeight::from_str(" R6.5 "), + Ok(LineHeight::Relative(6.5)) + ) + } + + #[test] + fn can_parse_with_a_prefix() { + assert_eq!( + LineHeight::from_str("a9.4 "), + Ok(LineHeight::Absolute(Pixels(9.4))) + ); + + assert_eq!( + LineHeight::from_str("A1.3"), + Ok(LineHeight::Absolute(Pixels(1.3))) + ) + } + + #[test] + fn cant_parse_with_missing_prefix() { + assert_eq!( + LineHeight::from_str("5.1"), + Err(ParseLineHeightError::MissingPrefix) + ) + } + + #[test] + fn cant_parse_invalid_prefix() { + assert_eq!( + LineHeight::from_str("g21"), + Err(ParseLineHeightError::InvalidPrefix) + ) + } + + #[test] + fn cant_parse_invalid_float() { + assert_eq!( + LineHeight::from_str("a2f"), + Err(ParseLineHeightError::ParseFloatError( + f32::from_str("2f").expect_err("float parse should fail") + )) + ) + } + + #[test] + fn cant_parse_empty_string() { + assert_eq!(LineHeight::from_str(" "), Err(ParseLineHeightError::Empty)) + } +} diff --git a/src/values/padding.rs b/src/values/padding.rs new file mode 100644 index 0000000..b6d3947 --- /dev/null +++ b/src/values/padding.rs @@ -0,0 +1,201 @@ +use std::num::ParseFloatError; +use std::str::FromStr; + +use iced::Padding; + +use super::Value; + +#[derive(Debug, thiserror::Error, Clone, PartialEq)] +pub enum ParsePaddingError { + #[error("wrong number of values: {0}, expected 1-4")] + WrongNumberOfValues(usize), + #[error("float parsing error: {0}")] + ParseFloatError(ParseFloatError), + #[error("missing bracket")] + MissingBracket, + #[error("cannot parse padding from empty string")] + Empty, +} + +impl From<ParseFloatError> for ParsePaddingError { + fn from(value: ParseFloatError) -> Self { + Self::ParseFloatError(value) + } +} + +impl Value for Padding { + type Err = ParsePaddingError; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + let s = s.trim(); + + if s.is_empty() { + return Err(ParsePaddingError::Empty); + } + + if !s.contains(['[', ',', ']']) { + let value = f32::from_str(s)?; + Ok(Padding { + top: value, + right: value, + bottom: value, + left: value, + }) + } else { + let values = s + .strip_prefix('[') + .and_then(|s| s.strip_suffix(']')) + .ok_or(ParsePaddingError::MissingBracket)? + .split(',') + .map(str::trim) + .map(f32::from_str) + .collect::<Result<Vec<_>, _>>()?; + + match values.len() { + 1 => Ok(Padding { + top: values[0], + right: values[0], + bottom: values[0], + left: values[0], + }), + 2 => Ok(Padding { + top: values[0], + right: values[1], + bottom: values[0], + left: values[1], + }), + 3 => Ok(Padding { + top: values[0], + right: values[1], + bottom: values[2], + left: values[1], + }), + 4 => Ok(Padding { + top: values[0], + right: values[1], + bottom: values[2], + left: values[3], + }), + other => Err(ParsePaddingError::WrongNumberOfValues(other)), + } + } + } + + fn to_string(&self) -> String { + format!( + "[{}, {}, {}, {}]", + self.top, self.right, self.bottom, self.left + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_parse_single_value() { + assert_eq!( + Padding::from_str("[1.5]"), + Ok(Padding { + top: 1.5, + right: 1.5, + bottom: 1.5, + left: 1.5, + }), + ) + } + + #[test] + fn can_parse_single_value_without_brackets() { + assert_eq!( + Padding::from_str("1.5"), + Ok(Padding { + top: 1.5, + right: 1.5, + bottom: 1.5, + left: 1.5, + }), + ) + } + + #[test] + fn can_parse_two_values() { + assert_eq!( + Padding::from_str("[3.2, 6.7]"), + Ok(Padding { + top: 3.2, + right: 6.7, + bottom: 3.2, + left: 6.7, + }), + ) + } + + #[test] + fn can_parse_three_values() { + assert_eq!( + Padding::from_str("[4.8, 8.1,5.9]"), + Ok(Padding { + top: 4.8, + right: 8.1, + bottom: 5.9, + left: 8.1, + }), + ) + } + + #[test] + fn can_parse_four_values() { + assert_eq!( + Padding::from_str("[35.4,74.6 ,53.1, 25.0]"), + Ok(Padding { + top: 35.4, + right: 74.6, + bottom: 53.1, + left: 25.0, + }), + ) + } + + #[test] + fn cant_parse_five_values() { + assert_eq!( + Padding::from_str("[1,2,3,4,5]"), + Err(ParsePaddingError::WrongNumberOfValues(5)), + ) + } + + #[test] + fn cant_parse_invalid_floats() { + assert_eq!( + Padding::from_str("[1f,2,3,4]"), + Err(ParsePaddingError::ParseFloatError( + f32::from_str("1f").expect_err("float parse should fail") + )) + ) + } + + #[test] + fn cant_parse_with_missing_bracket() { + assert_eq!( + Padding::from_str("1,2,3,4,5]"), + Err(ParsePaddingError::MissingBracket) + ); + + assert_eq!( + Padding::from_str("[1,2,3,4,5"), + Err(ParsePaddingError::MissingBracket) + ); + + assert_eq!( + Padding::from_str("1,2,3,4,5"), + Err(ParsePaddingError::MissingBracket) + ) + } + + #[test] + fn cant_parse_empty_string() { + assert_eq!(Padding::from_str(" "), Err(ParsePaddingError::Empty)) + } +} diff --git a/src/values/pixels.rs b/src/values/pixels.rs new file mode 100644 index 0000000..b2b0047 --- /dev/null +++ b/src/values/pixels.rs @@ -0,0 +1,18 @@ +use std::num::ParseFloatError; +use std::str::FromStr; + +use iced::Pixels; + +use super::Value; + +impl Value for Pixels { + type Err = ParseFloatError; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + Ok(Pixels(f32::from_str(s.trim())?)) + } + + fn to_string(&self) -> String { + self.0.to_string() + } +} diff --git a/src/values/rotation.rs b/src/values/rotation.rs new file mode 100755 index 0000000..90e3f84 --- /dev/null +++ b/src/values/rotation.rs @@ -0,0 +1,114 @@ +use std::num::ParseFloatError; +use std::str::FromStr; + +use iced::{Radians, Rotation}; + +use super::Value; + +#[derive(Debug, thiserror::Error, Clone, PartialEq)] +pub enum ParseRotationError { + #[error("float parsing error: {0}")] + ParseFloatError(ParseFloatError), + #[error("invalid prefix")] + InvalidPrefix, + #[error("cannot parse rotation from empty string")] + Empty, +} + +impl From<ParseFloatError> for ParseRotationError { + fn from(value: ParseFloatError) -> Self { + Self::ParseFloatError(value) + } +} + +impl Value for Rotation { + type Err = ParseRotationError; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + let s = s.trim(); + + if s.is_empty() { + return Err(ParseRotationError::Empty); + } + + if s.starts_with(|c: char| !c.is_ascii_digit()) { + let (prefix, value) = s.split_at(1); + match prefix.to_lowercase().as_str() { + "s" => Ok(Rotation::Solid(Radians(f32::from_str(value)?))), + "f" => Ok(Rotation::Floating(Radians(f32::from_str(value)?))), + _ => Err(ParseRotationError::InvalidPrefix), + } + } else { + Ok(Rotation::Floating(Radians(f32::from_str(s)?))) + } + } + + fn to_string(&self) -> String { + match self { + Self::Floating(value) => format!("f{}", value), + Self::Solid(value) => format!("s{}", value), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_parse_without_prefix() { + assert_eq!( + Rotation::from_str("10.5"), + Ok(Rotation::Floating(Radians(10.5))) + ) + } + + #[test] + fn can_parse_with_s_prefix() { + assert_eq!( + Rotation::from_str(" s12.3"), + Ok(Rotation::Solid(Radians(12.3))) + ); + + assert_eq!( + Rotation::from_str("S9.4"), + Ok(Rotation::Solid(Radians(9.4))) + ) + } + + #[test] + fn can_parse_with_f_prefix() { + assert_eq!( + Rotation::from_str("f16.9"), + Ok(Rotation::Floating(Radians(16.9))) + ); + + assert_eq!( + Rotation::from_str("F21.45 "), + Ok(Rotation::Floating(Radians(21.45))) + ) + } + + #[test] + fn cant_parse_invalid_prefix() { + assert_eq!( + Rotation::from_str("a6.0"), + Err(ParseRotationError::InvalidPrefix) + ) + } + + #[test] + fn cant_parse_invalid_float() { + assert_eq!( + Rotation::from_str("3.a"), + Err(ParseRotationError::ParseFloatError( + f32::from_str("3.a").expect_err("float parse should fail") + )) + ) + } + + #[test] + fn cant_parse_empty_string() { + assert_eq!(Rotation::from_str(" "), Err(ParseRotationError::Empty)) + } +} |
