ferroui_base/
corner_radius.rs1use std::fmt;
4use std::str::FromStr;
5
6use crate::utilities::math_utilities;
7use crate::utilities::span_helpers::InvariantF64;
8use crate::utilities::{FormatError, SpanStringTokenizer};
9
10#[derive(Clone, Copy, Debug, PartialEq, Default)]
12pub struct CornerRadius {
13 pub top_left: f64,
15 pub top_right: f64,
17 pub bottom_right: f64,
19 pub bottom_left: f64,
21}
22
23impl CornerRadius {
24 #[inline]
26 pub const fn new(top_left: f64, top_right: f64, bottom_right: f64, bottom_left: f64) -> Self {
27 Self {
28 top_left,
29 top_right,
30 bottom_right,
31 bottom_left,
32 }
33 }
34
35 #[inline]
37 pub const fn uniform(uniform_radius: f64) -> Self {
38 Self::new(
39 uniform_radius,
40 uniform_radius,
41 uniform_radius,
42 uniform_radius,
43 )
44 }
45
46 #[inline]
49 pub const fn top_bottom(top: f64, bottom: f64) -> Self {
50 Self::new(top, top, bottom, bottom)
51 }
52
53 #[inline]
56 pub fn is_uniform(&self) -> bool {
57 math_utilities::equals(self.top_left, self.top_right)
58 && math_utilities::equals(self.bottom_left, self.bottom_right)
59 && math_utilities::equals(self.top_right, self.bottom_right)
60 }
61
62 #[inline]
65 pub fn equals(&self, other: CornerRadius) -> bool {
66 self.top_left == other.top_left
67 && self.top_right == other.top_right
68 && self.bottom_right == other.bottom_right
69 && self.bottom_left == other.bottom_left
70 }
71
72 pub fn parse(s: &str) -> Result<Self, FormatError> {
74 const EXCEPTION_MESSAGE: &str = "Invalid CornerRadius.";
75
76 SpanStringTokenizer::with_message(s, EXCEPTION_MESSAGE).scope(|t| {
77 if let Some(a) = t.try_read_double()? {
78 if let Some(b) = t.try_read_double()? {
79 if let Some(c) = t.try_read_double()? {
80 return Ok(CornerRadius::new(a, b, c, t.read_double()?));
81 }
82
83 return Ok(CornerRadius::top_bottom(a, b));
84 }
85
86 return Ok(CornerRadius::uniform(a));
87 }
88
89 Err(FormatError::new(EXCEPTION_MESSAGE))
90 })
91 }
92}
93
94impl FromStr for CornerRadius {
95 type Err = FormatError;
96 #[inline]
97 fn from_str(s: &str) -> Result<Self, FormatError> {
98 CornerRadius::parse(s)
99 }
100}
101
102impl fmt::Display for CornerRadius {
103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104 write!(
105 f,
106 "{},{},{},{}",
107 InvariantF64(self.top_left),
108 InvariantF64(self.top_right),
109 InvariantF64(self.bottom_right),
110 InvariantF64(self.bottom_left)
111 )
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 #[test]
120 fn parse_parses_single_uniform_radius() {
121 let result = CornerRadius::parse("3.4").unwrap();
122
123 assert_eq!(CornerRadius::uniform(3.4), result);
124 }
125
126 #[test]
127 fn parse_parses_top_bottom() {
128 let result = CornerRadius::parse("1.1,2.2").unwrap();
129
130 assert_eq!(CornerRadius::top_bottom(1.1, 2.2), result);
131 }
132
133 #[test]
134 fn parse_parses_top_left_top_right_bottom_right_bottom_left() {
135 let result = CornerRadius::parse("1.1,2.2,3.3,4.4").unwrap();
136
137 assert_eq!(CornerRadius::new(1.1, 2.2, 3.3, 4.4), result);
138 }
139
140 #[test]
141 fn parse_accepts_spaces() {
142 let result = CornerRadius::parse("1.1 2.2 3.3 4.4").unwrap();
143
144 assert_eq!(CornerRadius::new(1.1, 2.2, 3.3, 4.4), result);
145 }
146
147 #[test]
150 fn display_uniform_and_errors() {
151 assert_eq!(
152 CornerRadius::new(1.0, 2.0, 3.0, 4.5).to_string(),
153 "1,2,3,4.5"
154 );
155 assert!(CornerRadius::uniform(3.0).is_uniform());
156 assert!(!CornerRadius::top_bottom(1.0, 2.0).is_uniform());
157 assert_eq!(
158 CornerRadius::parse("a").unwrap_err().message(),
159 "Invalid CornerRadius."
160 );
161 assert!(CornerRadius::parse("1,2,3").is_err());
162 }
163}