typst_library/foundations/
auto.rs1use std::fmt::{self, Debug, Formatter};
2
3use ecow::EcoString;
4
5use crate::diag::HintedStrResult;
6use crate::foundations::{
7 ty, CastInfo, Fold, FromValue, IntoValue, Reflect, Repr, Resolve, StyleChain, Type,
8 Value,
9};
10
11#[ty(cast, name = "auto")]
20#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
21pub struct AutoValue;
22
23impl IntoValue for AutoValue {
24 fn into_value(self) -> Value {
25 Value::Auto
26 }
27}
28
29impl FromValue for AutoValue {
30 fn from_value(value: Value) -> HintedStrResult<Self> {
31 match value {
32 Value::Auto => Ok(Self),
33 _ => Err(Self::error(&value)),
34 }
35 }
36}
37
38impl Reflect for AutoValue {
39 fn input() -> CastInfo {
40 CastInfo::Type(Type::of::<Self>())
41 }
42
43 fn output() -> CastInfo {
44 CastInfo::Type(Type::of::<Self>())
45 }
46
47 fn castable(value: &Value) -> bool {
48 matches!(value, Value::Auto)
49 }
50}
51
52impl Debug for AutoValue {
53 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
54 f.write_str("Auto")
55 }
56}
57
58impl Repr for AutoValue {
59 fn repr(&self) -> EcoString {
60 "auto".into()
61 }
62}
63
64#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
66pub enum Smart<T> {
67 Auto,
69 Custom(T),
71}
72
73impl<T> Smart<T> {
74 pub fn is_auto(&self) -> bool {
76 matches!(self, Self::Auto)
77 }
78
79 pub fn is_custom(&self) -> bool {
81 matches!(self, Self::Custom(_))
82 }
83
84 pub fn is_custom_and<F>(self, f: F) -> bool
86 where
87 F: Fn(T) -> bool,
88 {
89 match self {
90 Self::Auto => false,
91 Self::Custom(x) => f(x),
92 }
93 }
94
95 pub fn as_ref(&self) -> Smart<&T> {
97 match self {
98 Smart::Auto => Smart::Auto,
99 Smart::Custom(v) => Smart::Custom(v),
100 }
101 }
102
103 pub fn custom(self) -> Option<T> {
109 match self {
110 Self::Auto => None,
111 Self::Custom(x) => Some(x),
112 }
113 }
114
115 pub fn map<F, U>(self, f: F) -> Smart<U>
117 where
118 F: FnOnce(T) -> U,
119 {
120 match self {
121 Self::Auto => Smart::Auto,
122 Self::Custom(x) => Smart::Custom(f(x)),
123 }
124 }
125
126 pub fn map_or<F, U>(self, default: U, f: F) -> U
129 where
130 F: FnOnce(T) -> U,
131 {
132 match self {
133 Self::Auto => default,
134 Self::Custom(x) => f(x),
135 }
136 }
137
138 pub fn or(self, other: Smart<T>) -> Self {
140 match self {
141 Self::Custom(x) => Self::Custom(x),
142 Self::Auto => other,
143 }
144 }
145
146 pub fn or_else<F>(self, f: F) -> Self
149 where
150 F: FnOnce() -> Self,
151 {
152 match self {
153 Self::Custom(x) => Self::Custom(x),
154 Self::Auto => f(),
155 }
156 }
157
158 pub fn and_then<F, U>(self, f: F) -> Smart<U>
161 where
162 F: FnOnce(T) -> Smart<U>,
163 {
164 match self {
165 Smart::Auto => Smart::Auto,
166 Smart::Custom(x) => f(x),
167 }
168 }
169
170 pub fn unwrap_or(self, default: T) -> T {
172 match self {
173 Self::Auto => default,
174 Self::Custom(x) => x,
175 }
176 }
177
178 pub fn unwrap_or_else<F>(self, f: F) -> T
180 where
181 F: FnOnce() -> T,
182 {
183 match self {
184 Self::Auto => f(),
185 Self::Custom(x) => x,
186 }
187 }
188
189 pub fn unwrap_or_default(self) -> T
191 where
192 T: Default,
193 {
194 #[allow(clippy::unwrap_or_default)]
196 self.unwrap_or_else(T::default)
197 }
198}
199
200impl<T> Smart<Smart<T>> {
201 pub fn flatten(self) -> Smart<T> {
203 match self {
204 Smart::Custom(Smart::Auto) | Smart::Auto => Smart::Auto,
205 Smart::Custom(Smart::Custom(v)) => Smart::Custom(v),
206 }
207 }
208}
209
210impl<T> Default for Smart<T> {
211 fn default() -> Self {
212 Self::Auto
213 }
214}
215
216impl<T: Reflect> Reflect for Smart<T> {
217 fn input() -> CastInfo {
218 T::input() + AutoValue::input()
219 }
220
221 fn output() -> CastInfo {
222 T::output() + AutoValue::output()
223 }
224
225 fn castable(value: &Value) -> bool {
226 AutoValue::castable(value) || T::castable(value)
227 }
228}
229
230impl<T: IntoValue> IntoValue for Smart<T> {
231 fn into_value(self) -> Value {
232 match self {
233 Smart::Custom(v) => v.into_value(),
234 Smart::Auto => Value::Auto,
235 }
236 }
237}
238
239impl<T: FromValue> FromValue for Smart<T> {
240 fn from_value(value: Value) -> HintedStrResult<Self> {
241 match value {
242 Value::Auto => Ok(Self::Auto),
243 v if T::castable(&v) => Ok(Self::Custom(T::from_value(v)?)),
244 _ => Err(Self::error(&value)),
245 }
246 }
247}
248
249impl<T: Resolve> Resolve for Smart<T> {
250 type Output = Smart<T::Output>;
251
252 fn resolve(self, styles: StyleChain) -> Self::Output {
253 self.map(|v| v.resolve(styles))
254 }
255}
256
257impl<T: Fold> Fold for Smart<T> {
258 fn fold(self, outer: Self) -> Self {
259 use Smart::Custom;
260 match (self, outer) {
261 (Custom(inner), Custom(outer)) => Custom(inner.fold(outer)),
262 (inner, _) => inner,
265 }
266 }
267}