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