typst_library/foundations/content/
element.rs1use std::any::TypeId;
2use std::cmp::Ordering;
3use std::fmt::{self, Debug};
4use std::hash::Hash;
5use std::sync::OnceLock;
6
7use ecow::EcoString;
8use smallvec::SmallVec;
9use typst_utils::{DefSite, Static};
10
11use crate::diag::SourceResult;
12use crate::engine::Engine;
13use crate::foundations::{
14 Args, Content, ContentVtable, FieldAccessError, Func, NativeParamInfo, Repr, Scope,
15 Selector, StyleChain, Styles, Value, cast,
16};
17use crate::text::{Lang, Region};
18
19#[derive(Copy, Clone, Eq, PartialEq, Hash)]
21pub struct Element(Static<ContentVtable>);
22
23impl Element {
24 pub const fn of<T: NativeElement>() -> Self {
26 T::ELEM
27 }
28
29 pub const fn from_vtable(vtable: &'static ContentVtable) -> Self {
31 Self(Static(vtable))
32 }
33
34 pub fn name(self) -> &'static str {
36 self.vtable().name
37 }
38
39 pub fn title(&self) -> &'static str {
42 self.vtable().title
43 }
44
45 pub fn docs(&self) -> &'static str {
47 self.vtable().docs
48 }
49
50 pub fn def_site(&self) -> DefSite {
52 self.vtable().def_site
53 }
54
55 pub fn keywords(&self) -> &'static [&'static str] {
57 self.vtable().keywords
58 }
59
60 pub fn construct(
62 self,
63 engine: &mut Engine,
64 args: &mut Args,
65 ) -> SourceResult<Content> {
66 (self.vtable().construct)(engine, args)
67 }
68
69 pub fn set(self, engine: &mut Engine, mut args: Args) -> SourceResult<Styles> {
71 let styles = (self.vtable().set)(engine, &mut args)?;
72 args.finish()?;
73 Ok(styles)
74 }
75
76 pub fn can<C>(self) -> bool
78 where
79 C: ?Sized + 'static,
80 {
81 self.can_type_id(TypeId::of::<C>())
82 }
83
84 pub fn can_type_id(self, type_id: TypeId) -> bool {
87 (self.vtable().capability)(type_id).is_some()
88 }
89
90 pub fn select(self) -> Selector {
92 Selector::Elem(self, None)
93 }
94
95 pub fn where_(self, fields: SmallVec<[(u8, Value); 1]>) -> Selector {
98 Selector::Elem(self, Some(fields))
99 }
100
101 pub fn scope(&self) -> &'static Scope {
103 (self.vtable().store)().scope.get_or_init(|| (self.vtable().scope)())
104 }
105
106 pub fn params(&self) -> &'static [NativeParamInfo] {
108 (self.vtable().store)().params.get_or_init(|| {
109 self.vtable()
110 .fields
111 .iter()
112 .filter(|field| !field.synthesized)
113 .map(|field| NativeParamInfo {
114 name: field.name,
115 docs: field.docs,
116 def_site: Some(field.def_site),
117 input: (field.input)(),
118 default: field.default,
119 positional: field.positional,
120 named: !field.positional,
121 variadic: field.variadic,
122 required: field.required,
123 settable: field.settable,
124 })
125 .collect()
126 })
127 }
128
129 pub fn field_id(&self, name: &str) -> Option<u8> {
131 if name == "label" {
132 return Some(255);
133 }
134 (self.vtable().field_id)(name)
135 }
136
137 pub fn field_name(&self, id: u8) -> Option<&'static str> {
139 if id == 255 {
140 return Some("label");
141 }
142 self.vtable().field(id).map(|data| data.name)
143 }
144
145 pub fn field_from_styles(
147 &self,
148 id: u8,
149 styles: StyleChain,
150 ) -> Result<Value, FieldAccessError> {
151 self.vtable()
152 .field(id)
153 .and_then(|field| (field.get_from_styles)(styles))
154 .ok_or(FieldAccessError::Unknown)
155 }
156
157 pub fn local_name(&self, lang: Lang, region: Option<Region>) -> Option<&'static str> {
159 self.vtable().local_name.map(|f| f(lang, region))
160 }
161
162 pub(super) fn vtable(&self) -> &'static ContentVtable {
164 (self.0).0
165 }
166}
167
168impl Debug for Element {
169 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
170 write!(f, "Element({})", self.name())
171 }
172}
173
174impl Repr for Element {
175 fn repr(&self) -> EcoString {
176 self.name().into()
177 }
178}
179
180impl Ord for Element {
181 fn cmp(&self, other: &Self) -> Ordering {
182 self.name().cmp(other.name())
183 }
184}
185
186impl PartialOrd for Element {
187 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
188 Some(self.cmp(other))
189 }
190}
191
192cast! {
193 Element,
194 self => Value::Func(self.into()),
195 v: Func => v.to_element().ok_or("expected element")?,
196}
197
198#[derive(Default)]
200pub struct LazyElementStore {
201 pub scope: OnceLock<Scope>,
202 pub params: OnceLock<Vec<NativeParamInfo>>,
203}
204
205impl LazyElementStore {
206 pub const fn new() -> Self {
208 Self { scope: OnceLock::new(), params: OnceLock::new() }
209 }
210}
211
212pub unsafe trait NativeElement:
217 Debug + Clone + Hash + Construct + Set + Send + Sync + 'static
218{
219 const ELEM: Element;
221
222 fn pack(self) -> Content {
224 Content::new(self)
225 }
226}
227
228pub trait Construct {
230 fn construct(engine: &mut Engine, args: &mut Args) -> SourceResult<Content>
235 where
236 Self: Sized;
237}
238
239pub trait Set {
241 fn set(engine: &mut Engine, args: &mut Args) -> SourceResult<Styles>
243 where
244 Self: Sized;
245}
246
247pub trait Synthesize {
250 fn synthesize(&mut self, engine: &mut Engine, styles: StyleChain)
252 -> SourceResult<()>;
253}
254
255pub trait ShowSet {
260 fn show_set(&self, styles: StyleChain) -> Styles;
263}
264
265pub trait PlainText {
267 fn plain_text(&self, text: &mut EcoString);
269}