summaryrefslogtreecommitdiff
path: root/src/values
diff options
context:
space:
mode:
Diffstat (limited to 'src/values')
-rw-r--r--src/values/alignment.rs65
-rw-r--r--src/values/content_fit.rs69
-rw-r--r--src/values/length.rs145
-rw-r--r--src/values/line_height.rs117
-rw-r--r--src/values/padding.rs201
-rw-r--r--src/values/pixels.rs18
-rwxr-xr-xsrc/values/rotation.rs114
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))
+ }
+}