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::Static;
10
11use crate::diag::SourceResult;
12use crate::engine::Engine;
13use crate::foundations::{
14 Args, Content, ContentVtable, FieldAccessError, Func, ParamInfo, 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 keywords(&self) -> &'static [&'static str] {
52 self.vtable().keywords
53 }
54
55 pub fn construct(
57 self,
58 engine: &mut Engine,
59 args: &mut Args,
60 ) -> SourceResult<Content> {
61 (self.vtable().construct)(engine, args)
62 }
63
64 pub fn set(self, engine: &mut Engine, mut args: Args) -> SourceResult<Styles> {
66 let styles = (self.vtable().set)(engine, &mut args)?;
67 args.finish()?;
68 Ok(styles)
69 }
70
71 pub fn can<C>(self) -> bool
73 where
74 C: ?Sized + 'static,
75 {
76 self.can_type_id(TypeId::of::<C>())
77 }
78
79 pub fn can_type_id(self, type_id: TypeId) -> bool {
82 (self.vtable().capability)(type_id).is_some()
83 }
84
85 pub fn select(self) -> Selector {
87 Selector::Elem(self, None)
88 }
89
90 pub fn where_(self, fields: SmallVec<[(u8, Value); 1]>) -> Selector {
93 Selector::Elem(self, Some(fields))
94 }
95
96 pub fn scope(&self) -> &'static Scope {
98 (self.vtable().store)().scope.get_or_init(|| (self.vtable().scope)())
99 }
100
101 pub fn params(&self) -> &'static [ParamInfo] {
103 (self.vtable().store)().params.get_or_init(|| {
104 self.vtable()
105 .fields
106 .iter()
107 .filter(|field| !field.synthesized)
108 .map(|field| ParamInfo {
109 name: field.name,
110 docs: field.docs,
111 input: (field.input)(),
112 default: field.default,
113 positional: field.positional,
114 named: !field.positional,
115 variadic: field.variadic,
116 required: field.required,
117 settable: field.settable,
118 })
119 .collect()
120 })
121 }
122
123 pub fn field_id(&self, name: &str) -> Option<u8> {
125 if name == "label" {
126 return Some(255);
127 }
128 (self.vtable().field_id)(name)
129 }
130
131 pub fn field_name(&self, id: u8) -> Option<&'static str> {
133 if id == 255 {
134 return Some("label");
135 }
136 self.vtable().field(id).map(|data| data.name)
137 }
138
139 pub fn field_from_styles(
141 &self,
142 id: u8,
143 styles: StyleChain,
144 ) -> Result<Value, FieldAccessError> {
145 self.vtable()
146 .field(id)
147 .and_then(|field| (field.get_from_styles)(styles))
148 .ok_or(FieldAccessError::Unknown)
149 }
150
151 pub fn local_name(&self, lang: Lang, region: Option<Region>) -> Option<&'static str> {
153 self.vtable().local_name.map(|f| f(lang, region))
154 }
155
156 pub(super) fn vtable(&self) -> &'static ContentVtable {
158 (self.0).0
159 }
160}
161
162impl Debug for Element {
163 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
164 write!(f, "Element({})", self.name())
165 }
166}
167
168impl Repr for Element {
169 fn repr(&self) -> EcoString {
170 self.name().into()
171 }
172}
173
174impl Ord for Element {
175 fn cmp(&self, other: &Self) -> Ordering {
176 self.name().cmp(other.name())
177 }
178}
179
180impl PartialOrd for Element {
181 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
182 Some(self.cmp(other))
183 }
184}
185
186cast! {
187 Element,
188 self => Value::Func(self.into()),
189 v: Func => v.element().ok_or("expected element")?,
190}
191
192#[derive(Default)]
194pub struct LazyElementStore {
195 pub scope: OnceLock<Scope>,
196 pub params: OnceLock<Vec<ParamInfo>>,
197}
198
199impl LazyElementStore {
200 pub const fn new() -> Self {
202 Self { scope: OnceLock::new(), params: OnceLock::new() }
203 }
204}
205
206pub unsafe trait NativeElement:
211 Debug + Clone + Hash + Construct + Set + Send + Sync + 'static
212{
213 const ELEM: Element;
215
216 fn pack(self) -> Content {
218 Content::new(self)
219 }
220}
221
222pub trait Construct {
224 fn construct(engine: &mut Engine, args: &mut Args) -> SourceResult<Content>
229 where
230 Self: Sized;
231}
232
233pub trait Set {
235 fn set(engine: &mut Engine, args: &mut Args) -> SourceResult<Styles>
237 where
238 Self: Sized;
239}
240
241pub trait Synthesize {
244 fn synthesize(&mut self, engine: &mut Engine, styles: StyleChain)
246 -> SourceResult<()>;
247}
248
249pub trait ShowSet {
254 fn show_set(&self, styles: StyleChain) -> Styles;
257}
258
259pub trait PlainText {
261 fn plain_text(&self, text: &mut EcoString);
263}