1pub mod candidate;
2pub mod state;
3
4use std::{fmt::Debug, sync::Arc};
5
6use serde::Deserialize;
7use smallvec::{smallvec, SmallVec};
8use smol_str::{format_smolstr, SmolStr};
9
10use crate::{
11 common::MaybeArbitrary,
12 css::rule::RuleList,
13 ordering::OrderingKey,
14 process::{
15 ComposableHandler, RawValueRepr, RuleMatchingFn, ThemeParseError, Utility, UtilityGroup,
16 UtilityHandler, Variant, VariantHandlerExt,
17 },
18 theme::Theme,
19 types::TypeValidator,
20};
21
22#[derive(Debug, PartialEq, Clone, Copy, Default)]
23pub struct UtilityCandidate<'a> {
24 pub key: &'a str,
25 pub value: Option<MaybeArbitrary<'a>>,
26 pub modifier: Option<MaybeArbitrary<'a>>,
27 pub arbitrary: bool,
29 pub important: bool,
30 pub negative: bool,
31}
32
33impl<'a> UtilityCandidate<'a> {
34 pub fn with_key(key: &'a str) -> Self {
35 Self { key, ..Default::default() }
36 }
37
38 pub fn take_fraction(&self) -> Option<SmolStr> {
40 match (self.value, self.modifier) {
41 (Some(MaybeArbitrary::Named(v)), Some(MaybeArbitrary::Named(m))) => {
42 Some(format_smolstr!("{v}/{m}",))
43 }
44 _ => None,
45 }
46 }
47}
48
49#[derive(Debug, Deserialize)]
50#[serde(deny_unknown_fields, rename_all = "camelCase")]
51#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
52pub struct UtilityBuilder {
53 pub key: SmolStr,
55
56 #[serde(rename = "css")]
58 pub handler: Option<UtilityHandler>,
59
60 #[serde(default)]
62 pub modifier: Option<RawValueRepr>,
63
64 #[serde(rename = "theme")]
66 pub theme_key: Option<SmolStr>,
67
68 #[serde(rename = "type")]
72 pub validator: Option<Box<dyn TypeValidator>>,
73
74 #[serde(default)]
76 pub wrapper: Option<SmolStr>,
77
78 #[serde(default)]
80 pub supports_negative: bool,
81
82 #[serde(default)]
84 pub supports_fraction: bool,
85
86 #[serde(default)]
87 pub ordering_key: Option<OrderingKey>,
88
89 #[serde(skip_deserializing)]
91 pub additional_css: Option<Box<dyn AdditionalCssHandler>>,
92
93 #[serde(skip_deserializing)]
94 pub group: Option<UtilityGroup>,
95}
96
97pub trait AdditionalCssHandler: Sync + Send {
98 fn handle(&self, value: SmolStr) -> Option<Arc<RuleList>>;
99}
100
101impl<T: Fn(SmolStr) -> Option<RuleList> + Sync + Send> AdditionalCssHandler for T {
102 fn handle(&self, value: SmolStr) -> Option<Arc<RuleList>> {
103 self(value).map(Arc::new)
104 }
105}
106
107impl AdditionalCssHandler for Arc<RuleList> {
108 fn handle(&self, _value: SmolStr) -> Option<Arc<RuleList>> {
109 Some(self.clone())
110 }
111}
112
113impl Debug for dyn AdditionalCssHandler {
114 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
115 f.debug_struct("AdditionalCssHandler").field("address", &format!("{:p}", self)).finish()
116 }
117}
118
119impl UtilityBuilder {
120 pub fn new(key: impl Into<SmolStr>, handler: impl RuleMatchingFn + 'static) -> Self {
121 Self {
122 key: key.into(),
123 handler: Some(UtilityHandler::new(handler)),
124 theme_key: None,
125 supports_negative: false,
126 supports_fraction: false,
127 modifier: None,
128 validator: None,
129 additional_css: None,
130 wrapper: None,
131 ordering_key: None,
132 group: None,
133 }
134 }
135
136 pub fn parse(self, theme: &Theme) -> Result<(SmolStr, Utility), ThemeParseError> {
137 Ok((
138 self.key,
139 Utility {
140 handler: self.handler.unwrap(),
141 supports_negative: self.supports_negative,
142 supports_fraction: self.supports_fraction,
143 value_repr: RawValueRepr { theme_key: self.theme_key, validator: self.validator }
144 .parse(theme)?,
145 modifier: self.modifier.map(|m| m.parse(theme)).transpose()?,
146 wrapper: self.wrapper,
147 additional_css: self.additional_css,
148 ordering_key: self.ordering_key,
149 group: self.group,
150 },
151 ))
152 }
153
154 pub fn with_theme(&mut self, key: impl Into<SmolStr>) -> &mut Self {
155 self.theme_key = Some(key.into());
156 self
157 }
158
159 pub fn support_negative(&mut self) -> &mut Self {
160 self.supports_negative = true;
161 self
162 }
163
164 pub fn support_fraction(&mut self) -> &mut Self {
165 self.supports_fraction = true;
166 self
167 }
168
169 pub fn with_modifier(&mut self, modifier: RawValueRepr) -> &mut Self {
170 self.modifier = Some(modifier);
171 self
172 }
173
174 pub fn with_validator(&mut self, validator: impl TypeValidator + 'static) -> &mut Self {
175 self.validator = Some(Box::new(validator));
176 self
177 }
178
179 pub fn with_additional_css(&mut self, css: impl AdditionalCssHandler + 'static) -> &mut Self {
180 self.additional_css = Some(Box::new(css));
181 self
182 }
183
184 pub fn with_wrapper(&mut self, wrapper: &str) -> &mut Self {
185 self.wrapper = Some(wrapper.into());
186 self
187 }
188
189 pub fn with_ordering(&mut self, key: OrderingKey) -> &mut Self {
190 self.ordering_key = Some(key);
191 self
192 }
193
194 pub fn with_group(&mut self, group: UtilityGroup) -> &mut Self {
195 self.group = Some(group);
196 self
197 }
198}
199
200#[derive(Debug, Clone)]
201pub struct VariantCandidate<'a> {
202 pub key: &'a str,
203 pub value: Option<MaybeArbitrary<'a>>,
204 pub modifier: Option<MaybeArbitrary<'a>>,
205 pub arbitrary: bool,
207 pub processor: Variant,
208 pub layers: SmallVec<[ComposableHandler; 1]>,
209}
210
211impl<'a> VariantCandidate<'a> {
212 pub fn new(processor: Variant, key: &'a str) -> Self {
213 Self { key, value: None, modifier: None, arbitrary: false, processor, layers: smallvec![] }
214 }
215
216 pub fn with_value(mut self, value: Option<MaybeArbitrary<'a>>) -> Self {
217 self.value = value;
218 self
219 }
220
221 pub fn with_modifier(mut self, modifier: Option<MaybeArbitrary<'a>>) -> Self {
222 self.modifier = modifier;
223 self
224 }
225
226 pub fn with_layers(mut self, layers: SmallVec<[ComposableHandler; 1]>) -> Self {
227 self.layers = layers;
228 self
229 }
230
231 pub fn arbitrary(mut self) -> Self {
232 self.arbitrary = true;
233 self
234 }
235
236 pub fn handle(&self, rule: RuleList) -> RuleList {
237 let rule = self.processor.handle(self, rule);
238 self.layers.iter().rev().fold(rule, |rule, handler| handler.handle(self, rule))
239 }
240}