typst_library/foundations/
element.rs1use std::any::TypeId;
2use std::cmp::Ordering;
3use std::fmt::{self, Debug};
4use std::hash::Hash;
5use std::ptr::NonNull;
6use std::sync::LazyLock;
7
8use ecow::EcoString;
9use smallvec::SmallVec;
10#[doc(inline)]
11pub use typst_macros::elem;
12use typst_utils::Static;
13
14use crate::diag::SourceResult;
15use crate::engine::Engine;
16use crate::foundations::{
17 cast, Args, Content, Dict, FieldAccessError, Func, ParamInfo, Repr, Scope, Selector,
18 StyleChain, Styles, Value,
19};
20use crate::text::{Lang, Region};
21
22#[derive(Copy, Clone, Eq, PartialEq, Hash)]
24pub struct Element(Static<NativeElementData>);
25
26impl Element {
27 pub fn of<T: NativeElement>() -> Self {
29 T::elem()
30 }
31
32 pub fn name(self) -> &'static str {
34 self.0.name
35 }
36
37 pub fn title(&self) -> &'static str {
40 self.0.title
41 }
42
43 pub fn docs(&self) -> &'static str {
45 self.0.docs
46 }
47
48 pub fn keywords(&self) -> &'static [&'static str] {
50 self.0.keywords
51 }
52
53 pub fn construct(
55 self,
56 engine: &mut Engine,
57 args: &mut Args,
58 ) -> SourceResult<Content> {
59 (self.0.construct)(engine, args)
60 }
61
62 pub fn set(self, engine: &mut Engine, mut args: Args) -> SourceResult<Styles> {
64 let styles = (self.0.set)(engine, &mut args)?;
65 args.finish()?;
66 Ok(styles)
67 }
68
69 pub fn can<C>(self) -> bool
71 where
72 C: ?Sized + 'static,
73 {
74 self.can_type_id(TypeId::of::<C>())
75 }
76
77 pub fn can_type_id(self, type_id: TypeId) -> bool {
80 (self.0.vtable)(type_id).is_some()
81 }
82
83 pub fn vtable(self) -> fn(of: TypeId) -> Option<NonNull<()>> {
85 self.0.vtable
86 }
87
88 pub fn select(self) -> Selector {
90 Selector::Elem(self, None)
91 }
92
93 pub fn where_(self, fields: SmallVec<[(u8, Value); 1]>) -> Selector {
96 Selector::Elem(self, Some(fields))
97 }
98
99 pub fn scope(&self) -> &'static Scope {
101 &(self.0).0.scope
102 }
103
104 pub fn params(&self) -> &'static [ParamInfo] {
106 &(self.0).0.params
107 }
108
109 pub fn field_id(&self, name: &str) -> Option<u8> {
111 if name == "label" {
112 return Some(255);
113 }
114 (self.0.field_id)(name)
115 }
116
117 pub fn field_name(&self, id: u8) -> Option<&'static str> {
119 if id == 255 {
120 return Some("label");
121 }
122 (self.0.field_name)(id)
123 }
124
125 pub fn field_from_styles(
127 &self,
128 id: u8,
129 styles: StyleChain,
130 ) -> Result<Value, FieldAccessError> {
131 (self.0.field_from_styles)(id, styles)
132 }
133
134 pub fn local_name(&self, lang: Lang, region: Option<Region>) -> Option<&'static str> {
136 (self.0).0.local_name.map(|f| f(lang, region))
137 }
138}
139
140impl Debug for Element {
141 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
142 write!(f, "Element({})", self.name())
143 }
144}
145
146impl Repr for Element {
147 fn repr(&self) -> EcoString {
148 self.name().into()
149 }
150}
151
152impl Ord for Element {
153 fn cmp(&self, other: &Self) -> Ordering {
154 self.name().cmp(other.name())
155 }
156}
157
158impl PartialOrd for Element {
159 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
160 Some(self.cmp(other))
161 }
162}
163
164cast! {
165 Element,
166 self => Value::Func(self.into()),
167 v: Func => v.element().ok_or("expected element")?,
168}
169
170pub trait NativeElement:
172 Debug
173 + Clone
174 + PartialEq
175 + Hash
176 + Construct
177 + Set
178 + Capable
179 + Fields
180 + Repr
181 + Send
182 + Sync
183 + 'static
184{
185 fn elem() -> Element
187 where
188 Self: Sized,
189 {
190 Element::from(Self::data())
191 }
192
193 fn pack(self) -> Content
195 where
196 Self: Sized,
197 {
198 Content::new(self)
199 }
200
201 fn data() -> &'static NativeElementData
203 where
204 Self: Sized;
205}
206
207pub unsafe trait Capable {
214 fn vtable(capability: TypeId) -> Option<NonNull<()>>;
216}
217
218pub trait Fields {
220 type Enum
222 where
223 Self: Sized;
224
225 fn has(&self, id: u8) -> bool;
227
228 fn field(&self, id: u8) -> Result<Value, FieldAccessError>;
230
231 fn field_with_styles(
233 &self,
234 id: u8,
235 styles: StyleChain,
236 ) -> Result<Value, FieldAccessError>;
237
238 fn field_from_styles(id: u8, styles: StyleChain) -> Result<Value, FieldAccessError>
240 where
241 Self: Sized;
242
243 fn materialize(&mut self, styles: StyleChain);
245
246 fn fields(&self) -> Dict;
248}
249
250pub trait Construct {
252 fn construct(engine: &mut Engine, args: &mut Args) -> SourceResult<Content>
257 where
258 Self: Sized;
259}
260
261pub trait Set {
263 fn set(engine: &mut Engine, args: &mut Args) -> SourceResult<Styles>
265 where
266 Self: Sized;
267}
268
269#[derive(Debug)]
271pub struct NativeElementData {
272 pub name: &'static str,
274 pub title: &'static str,
276 pub docs: &'static str,
278 pub keywords: &'static [&'static str],
280 pub construct: fn(&mut Engine, &mut Args) -> SourceResult<Content>,
282 pub set: fn(&mut Engine, &mut Args) -> SourceResult<Styles>,
284 pub vtable: fn(capability: TypeId) -> Option<NonNull<()>>,
287 pub field_id: fn(name: &str) -> Option<u8>,
289 pub field_name: fn(u8) -> Option<&'static str>,
291 pub field_from_styles: fn(u8, StyleChain) -> Result<Value, FieldAccessError>,
293 pub local_name: Option<fn(Lang, Option<Region>) -> &'static str>,
295 pub scope: LazyLock<Scope>,
296 pub params: LazyLock<Vec<ParamInfo>>,
298}
299
300impl From<&'static NativeElementData> for Element {
301 fn from(data: &'static NativeElementData) -> Self {
302 Self(Static(data))
303 }
304}
305
306cast! {
307 &'static NativeElementData,
308 self => Element::from(self).into_value(),
309}
310
311pub trait Synthesize {
314 fn synthesize(&mut self, engine: &mut Engine, styles: StyleChain)
316 -> SourceResult<()>;
317}
318
319pub trait Show {
321 fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content>;
323}
324
325pub trait ShowSet {
330 fn show_set(&self, styles: StyleChain) -> Styles;
333}