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