1use std::fmt::{self, Debug, Formatter};
2
3use typst_utils::Get;
4
5use crate::diag::HintedStrResult;
6use crate::foundations::{
7 AlternativeFold, CastInfo, Dict, Fold, FromValue, IntoValue, Reflect, Resolve,
8 StyleChain, Value,
9};
10use crate::layout::Side;
11
12#[derive(Default, Copy, Clone, Eq, PartialEq, Hash)]
14pub struct Corners<T> {
15 pub top_left: T,
17 pub top_right: T,
19 pub bottom_right: T,
21 pub bottom_left: T,
23}
24
25impl<T> Corners<T> {
26 pub const fn new(top_left: T, top_right: T, bottom_right: T, bottom_left: T) -> Self {
28 Self { top_left, top_right, bottom_right, bottom_left }
29 }
30
31 pub fn splat(value: T) -> Self
33 where
34 T: Clone,
35 {
36 Self {
37 top_left: value.clone(),
38 top_right: value.clone(),
39 bottom_right: value.clone(),
40 bottom_left: value,
41 }
42 }
43
44 pub fn map<F, U>(self, mut f: F) -> Corners<U>
46 where
47 F: FnMut(T) -> U,
48 {
49 Corners {
50 top_left: f(self.top_left),
51 top_right: f(self.top_right),
52 bottom_right: f(self.bottom_right),
53 bottom_left: f(self.bottom_left),
54 }
55 }
56
57 pub fn zip<U>(self, other: Corners<U>) -> Corners<(T, U)> {
59 Corners {
60 top_left: (self.top_left, other.top_left),
61 top_right: (self.top_right, other.top_right),
62 bottom_right: (self.bottom_right, other.bottom_right),
63 bottom_left: (self.bottom_left, other.bottom_left),
64 }
65 }
66
67 pub fn iter(&self) -> impl Iterator<Item = &T> {
70 [&self.top_left, &self.top_right, &self.bottom_right, &self.bottom_left]
71 .into_iter()
72 }
73
74 pub fn is_uniform(&self) -> bool
76 where
77 T: PartialEq,
78 {
79 self.top_left == self.top_right
80 && self.top_right == self.bottom_right
81 && self.bottom_right == self.bottom_left
82 }
83}
84
85impl<T> Corners<Option<T>> {
86 pub fn unwrap_or_default(self) -> Corners<T>
88 where
89 T: Default,
90 {
91 self.map(Option::unwrap_or_default)
92 }
93}
94
95impl<T> Get<Corner> for Corners<T> {
96 type Component = T;
97
98 fn get_ref(&self, corner: Corner) -> &T {
99 match corner {
100 Corner::TopLeft => &self.top_left,
101 Corner::TopRight => &self.top_right,
102 Corner::BottomRight => &self.bottom_right,
103 Corner::BottomLeft => &self.bottom_left,
104 }
105 }
106
107 fn get_mut(&mut self, corner: Corner) -> &mut T {
108 match corner {
109 Corner::TopLeft => &mut self.top_left,
110 Corner::TopRight => &mut self.top_right,
111 Corner::BottomRight => &mut self.bottom_right,
112 Corner::BottomLeft => &mut self.bottom_left,
113 }
114 }
115}
116
117impl<T: Debug + PartialEq> Debug for Corners<T> {
118 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
119 if self.is_uniform() {
120 f.write_str("Corners::splat(")?;
121 self.top_left.fmt(f)?;
122 f.write_str(")")
123 } else {
124 f.debug_struct("Corners")
125 .field("top_left", &self.top_left)
126 .field("top_right", &self.top_right)
127 .field("bottom_right", &self.bottom_right)
128 .field("bottom_left", &self.bottom_left)
129 .finish()
130 }
131 }
132}
133
134impl<T: Reflect> Reflect for Corners<Option<T>> {
135 fn input() -> CastInfo {
136 T::input() + Dict::input()
137 }
138
139 fn output() -> CastInfo {
140 T::output() + Dict::output()
141 }
142
143 fn castable(value: &Value) -> bool {
144 Dict::castable(value) || T::castable(value)
145 }
146}
147
148impl<T> IntoValue for Corners<Option<T>>
149where
150 T: PartialEq + IntoValue,
151{
152 fn into_value(self) -> Value {
153 if self.is_uniform() {
154 if let Some(top_left) = self.top_left {
155 return top_left.into_value();
156 }
157 }
158
159 let mut dict = Dict::new();
160 let mut handle = |key: &str, component: Option<T>| {
161 if let Some(c) = component {
162 dict.insert(key.into(), c.into_value());
163 }
164 };
165
166 handle("top-left", self.top_left);
167 handle("top-right", self.top_right);
168 handle("bottom-right", self.bottom_right);
169 handle("bottom-left", self.bottom_left);
170
171 Value::Dict(dict)
172 }
173}
174
175impl<T> FromValue for Corners<Option<T>>
176where
177 T: FromValue + Clone,
178{
179 fn from_value(mut value: Value) -> HintedStrResult<Self> {
180 let expected_keys = [
181 "top-left",
182 "top-right",
183 "bottom-right",
184 "bottom-left",
185 "left",
186 "top",
187 "right",
188 "bottom",
189 "rest",
190 ];
191
192 if let Value::Dict(dict) = &mut value {
193 if dict.is_empty() {
194 return Ok(Self::splat(None));
195 } else if dict.iter().any(|(key, _)| expected_keys.contains(&key.as_str())) {
196 let mut take = |key| dict.take(key).ok().map(T::from_value).transpose();
197 let rest = take("rest")?;
198 let left = take("left")?.or_else(|| rest.clone());
199 let top = take("top")?.or_else(|| rest.clone());
200 let right = take("right")?.or_else(|| rest.clone());
201 let bottom = take("bottom")?.or_else(|| rest.clone());
202 let corners = Corners {
203 top_left: take("top-left")?
204 .or_else(|| top.clone())
205 .or_else(|| left.clone()),
206 top_right: take("top-right")?
207 .or_else(|| top.clone())
208 .or_else(|| right.clone()),
209 bottom_right: take("bottom-right")?
210 .or_else(|| bottom.clone())
211 .or_else(|| right.clone()),
212 bottom_left: take("bottom-left")?
213 .or_else(|| bottom.clone())
214 .or_else(|| left.clone()),
215 };
216
217 dict.finish(&expected_keys)?;
218 return Ok(corners);
219 }
220 }
221
222 if T::castable(&value) {
223 Ok(Self::splat(Some(T::from_value(value)?)))
224 } else if let Value::Dict(dict) = &value {
225 let keys = dict.iter().map(|kv| kv.0.as_str()).collect();
226 Err(Dict::unexpected_keys(keys, None).into())
229 } else {
230 Err(Self::error(&value))
231 }
232 }
233}
234
235impl<T: Resolve> Resolve for Corners<T> {
236 type Output = Corners<T::Output>;
237
238 fn resolve(self, styles: StyleChain) -> Self::Output {
239 self.map(|v| v.resolve(styles))
240 }
241}
242
243impl<T: Fold> Fold for Corners<Option<T>> {
244 fn fold(self, outer: Self) -> Self {
245 self.zip(outer).map(|(inner, outer)| inner.fold_or(outer))
249 }
250}
251
252#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
254pub enum Corner {
255 TopLeft,
257 TopRight,
259 BottomRight,
261 BottomLeft,
263}
264
265impl Corner {
266 pub fn inv(self) -> Self {
268 match self {
269 Self::TopLeft => Self::BottomRight,
270 Self::TopRight => Self::BottomLeft,
271 Self::BottomRight => Self::TopLeft,
272 Self::BottomLeft => Self::TopRight,
273 }
274 }
275
276 pub fn next_cw(self) -> Self {
278 match self {
279 Self::TopLeft => Self::TopRight,
280 Self::TopRight => Self::BottomRight,
281 Self::BottomRight => Self::BottomLeft,
282 Self::BottomLeft => Self::TopLeft,
283 }
284 }
285
286 pub fn next_ccw(self) -> Self {
288 match self {
289 Self::TopLeft => Self::BottomLeft,
290 Self::TopRight => Self::TopLeft,
291 Self::BottomRight => Self::TopRight,
292 Self::BottomLeft => Self::BottomRight,
293 }
294 }
295
296 pub fn side_cw(self) -> Side {
298 match self {
299 Self::TopLeft => Side::Top,
300 Self::TopRight => Side::Right,
301 Self::BottomRight => Side::Bottom,
302 Self::BottomLeft => Side::Left,
303 }
304 }
305
306 pub fn side_ccw(self) -> Side {
308 match self {
309 Self::TopLeft => Side::Left,
310 Self::TopRight => Side::Top,
311 Self::BottomRight => Side::Right,
312 Self::BottomLeft => Side::Bottom,
313 }
314 }
315}