typst_library/foundations/
selector.rs1use std::any::{Any, TypeId};
2use std::sync::Arc;
3
4use comemo::Tracked;
5use ecow::{EcoString, EcoVec, eco_format};
6use smallvec::SmallVec;
7
8use crate::diag::{HintedStrResult, StrResult, bail};
9use crate::foundations::{
10 CastInfo, Content, Context, Dict, Element, FromValue, Func, Label, Reflect, Regex,
11 Repr, Str, StyleChain, Symbol, Type, Value, cast, func, repr, scope, ty,
12};
13use crate::introspection::{Introspector, Locatable, Location, Unqueriable};
14
15#[macro_export]
17#[doc(hidden)]
18macro_rules! __select_where {
19 ($ty:ty $(, $field:ident => $value:expr)* $(,)?) => {{
20 #[allow(unused_mut)]
21 let mut fields = $crate::foundations::SmallVec::new();
22 $(
23 fields.push((
24 <$ty>::$field.index(),
25 $crate::foundations::IntoValue::into_value($value),
26 ));
27 )*
28 $crate::foundations::Selector::Elem(
29 <$ty as $crate::foundations::NativeElement>::ELEM,
30 Some(fields),
31 )
32 }};
33}
34
35#[doc(inline)]
36pub use crate::__select_where as select_where;
37
38#[ty(scope, cast)]
72#[derive(Debug, Clone, PartialEq, Hash)]
73pub enum Selector {
74 Elem(Element, Option<SmallVec<[(u8, Value); 1]>>),
79 Location(Location),
81 Label(Label),
83 Regex(Regex),
85 Can(TypeId),
87 Or(EcoVec<Self>),
89 And(EcoVec<Self>),
91 Before { selector: Arc<Self>, end: Arc<Self>, inclusive: bool },
93 After { selector: Arc<Self>, start: Arc<Self>, inclusive: bool },
95}
96
97impl Selector {
98 pub fn text(text: &str) -> StrResult<Self> {
100 if text.is_empty() {
101 bail!("text selector is empty");
102 }
103 Ok(Self::Regex(Regex::new(®ex::escape(text)).unwrap()))
104 }
105
106 pub fn regex(regex: Regex) -> StrResult<Self> {
108 if regex.as_str().is_empty() {
109 bail!("regex selector is empty");
110 }
111 if regex.is_match("") {
112 bail!("regex matches empty text");
113 }
114 Ok(Self::Regex(regex))
115 }
116
117 pub fn can<T: ?Sized + Any>() -> Self {
119 Self::Can(TypeId::of::<T>())
120 }
121
122 pub fn matches(&self, target: &Content, styles: Option<StyleChain>) -> bool {
124 match self {
125 Self::Elem(element, dict) => {
126 target.elem() == *element
127 && dict.iter().flat_map(|dict| dict.iter()).all(|(id, value)| {
128 target.get(*id, styles).as_ref().ok() == Some(value)
129 })
130 }
131 Self::Label(label) => target.label() == Some(*label),
132 Self::Can(cap) => target.func().can_type_id(*cap),
133 Self::Or(selectors) => {
134 selectors.iter().any(move |sel| sel.matches(target, styles))
135 }
136 Self::And(selectors) => {
137 selectors.iter().all(move |sel| sel.matches(target, styles))
138 }
139 Self::Location(location) => target.location() == Some(*location),
140 Self::Regex(_) | Self::Before { .. } | Self::After { .. } => false,
142 }
143 }
144}
145
146#[scope]
147impl Selector {
148 #[func(constructor)]
155 pub fn construct(
156 target: Selector,
159 ) -> Selector {
160 target
161 }
162
163 #[func]
165 pub fn or(
166 self,
167 #[variadic]
169 others: Vec<Selector>,
170 ) -> Selector {
171 Self::Or(others.into_iter().chain(Some(self)).collect())
172 }
173
174 #[func]
176 pub fn and(
177 self,
178 #[variadic]
180 others: Vec<Selector>,
181 ) -> Selector {
182 Self::And(others.into_iter().chain(Some(self)).collect())
183 }
184
185 #[func]
188 pub fn before(
189 self,
190 end: LocatableSelector,
192 #[named]
195 #[default(true)]
196 inclusive: bool,
197 ) -> Selector {
198 Self::Before {
199 selector: Arc::new(self),
200 end: Arc::new(end.0),
201 inclusive,
202 }
203 }
204
205 #[func]
208 pub fn after(
209 self,
210 start: LocatableSelector,
212 #[named]
216 #[default(true)]
217 inclusive: bool,
218 ) -> Selector {
219 Self::After {
220 selector: Arc::new(self),
221 start: Arc::new(start.0),
222 inclusive,
223 }
224 }
225}
226
227impl From<Location> for Selector {
228 fn from(value: Location) -> Self {
229 Self::Location(value)
230 }
231}
232
233impl Repr for Selector {
234 fn repr(&self) -> EcoString {
235 match self {
236 Self::Elem(elem, dict) => {
237 if let Some(dict) = dict {
238 let dict = dict
239 .iter()
240 .map(|(id, value)| (elem.field_name(*id).unwrap(), value.clone()))
241 .map(|(name, value)| (EcoString::from(name).into(), value))
242 .collect::<Dict>();
243 eco_format!("{}.where{}", elem.name(), dict.repr())
244 } else {
245 elem.name().into()
246 }
247 }
248 Self::Label(label) => label.repr(),
249 Self::Regex(regex) => regex.repr(),
250 Self::Can(cap) => eco_format!("{cap:?}"),
251 Self::Or(selectors) | Self::And(selectors) => {
252 let function = if matches!(self, Self::Or(_)) { "or" } else { "and" };
253 let pieces: Vec<_> = selectors.iter().map(Selector::repr).collect();
254 eco_format!("{}{}", function, repr::pretty_array_like(&pieces, false))
255 }
256 Self::Location(loc) => loc.repr(),
257 Self::Before { selector, end: split, inclusive }
258 | Self::After { selector, start: split, inclusive } => {
259 let method =
260 if matches!(self, Self::Before { .. }) { "before" } else { "after" };
261 let inclusive_arg = if !*inclusive { ", inclusive: false" } else { "" };
262 eco_format!(
263 "{}.{}({}{})",
264 selector.repr(),
265 method,
266 split.repr(),
267 inclusive_arg
268 )
269 }
270 }
271 }
272}
273
274cast! {
275 type Selector,
276 text: EcoString => Self::text(&text)?,
277 func: Func => func
278 .element()
279 .ok_or("only element functions can be used as selectors")?
280 .select(),
281 label: Label => Self::Label(label),
282 regex: Regex => Self::regex(regex)?,
283 location: Location => Self::Location(location),
284}
285
286#[derive(Debug, Clone, PartialEq, Hash)]
291pub struct LocatableSelector(pub Selector);
292
293impl LocatableSelector {
294 pub fn resolve_unique(
296 &self,
297 introspector: Tracked<Introspector>,
298 context: Tracked<Context>,
299 ) -> HintedStrResult<Location> {
300 match &self.0 {
301 Selector::Location(loc) => Ok(*loc),
302 other => {
303 context.introspect()?;
304 Ok(introspector.query_unique(other).map(|c| c.location().unwrap())?)
305 }
306 }
307 }
308}
309
310impl Reflect for LocatableSelector {
311 fn input() -> CastInfo {
312 CastInfo::Union(vec![
313 CastInfo::Type(Type::of::<Label>()),
314 CastInfo::Type(Type::of::<Func>()),
315 CastInfo::Type(Type::of::<Location>()),
316 CastInfo::Type(Type::of::<Selector>()),
317 ])
318 }
319
320 fn output() -> CastInfo {
321 CastInfo::Type(Type::of::<Selector>())
322 }
323
324 fn castable(value: &Value) -> bool {
325 Label::castable(value)
326 || Func::castable(value)
327 || Location::castable(value)
328 || Selector::castable(value)
329 }
330}
331
332cast! {
333 LocatableSelector,
334 self => self.0.into_value(),
335}
336
337impl FromValue for LocatableSelector {
338 fn from_value(value: Value) -> HintedStrResult<Self> {
339 fn validate(selector: &Selector) -> StrResult<()> {
340 match selector {
341 Selector::Elem(elem, _) => {
342 if !elem.can::<dyn Locatable>() || elem.can::<dyn Unqueriable>() {
343 Err(eco_format!("{} is not locatable", elem.name()))?
344 }
345 }
346 Selector::Location(_) => {}
347 Selector::Label(_) => {}
348 Selector::Regex(_) => bail!("text is not locatable"),
349 Selector::Can(_) => bail!("capability is not locatable"),
350 Selector::Or(list) | Selector::And(list) => {
351 for selector in list {
352 validate(selector)?;
353 }
354 }
355 Selector::Before { selector, end: split, .. }
356 | Selector::After { selector, start: split, .. } => {
357 for selector in [selector, split] {
358 validate(selector)?;
359 }
360 }
361 }
362 Ok(())
363 }
364
365 if !Self::castable(&value) {
366 return Err(Self::error(&value));
367 }
368
369 let selector = Selector::from_value(value)?;
370 validate(&selector)?;
371 Ok(Self(selector))
372 }
373}
374
375impl From<Location> for LocatableSelector {
376 fn from(loc: Location) -> Self {
377 Self(Selector::Location(loc))
378 }
379}
380
381#[derive(Clone, PartialEq, Hash)]
386pub struct ShowableSelector(pub Selector);
387
388impl Reflect for ShowableSelector {
389 fn input() -> CastInfo {
390 CastInfo::Union(vec![
391 CastInfo::Type(Type::of::<Symbol>()),
392 CastInfo::Type(Type::of::<Str>()),
393 CastInfo::Type(Type::of::<Label>()),
394 CastInfo::Type(Type::of::<Func>()),
395 CastInfo::Type(Type::of::<Regex>()),
396 CastInfo::Type(Type::of::<Selector>()),
397 ])
398 }
399
400 fn output() -> CastInfo {
401 CastInfo::Type(Type::of::<Selector>())
402 }
403
404 fn castable(value: &Value) -> bool {
405 Symbol::castable(value)
406 || Str::castable(value)
407 || Label::castable(value)
408 || Func::castable(value)
409 || Regex::castable(value)
410 || Selector::castable(value)
411 }
412}
413
414cast! {
415 ShowableSelector,
416 self => self.0.into_value(),
417}
418
419impl FromValue for ShowableSelector {
420 fn from_value(value: Value) -> HintedStrResult<Self> {
421 fn validate(selector: &Selector, nested: bool) -> HintedStrResult<()> {
422 match selector {
423 Selector::Elem(_, _) => {}
424 Selector::Label(_) => {}
425 Selector::Regex(_) if !nested => {}
426 Selector::Or(list) | Selector::And(list) => {
427 for selector in list {
428 validate(selector, true)?;
429 }
430 }
431 Selector::Regex(_)
432 | Selector::Location(_)
433 | Selector::Can(_)
434 | Selector::Before { .. }
435 | Selector::After { .. } => {
436 bail!("this selector cannot be used with show")
437 }
438 }
439 Ok(())
440 }
441
442 if !Self::castable(&value) {
443 return Err(Self::error(&value));
444 }
445
446 let selector = Selector::from_value(value)?;
447 validate(&selector, false)?;
448 Ok(Self(selector))
449 }
450}