typst_macros/
lib.rs

1//! Procedural macros for Typst.
2
3extern crate proc_macro;
4
5#[macro_use]
6mod util;
7mod cast;
8mod elem;
9mod func;
10mod scope;
11mod time;
12mod ty;
13
14use proc_macro::TokenStream as BoundaryStream;
15use syn::DeriveInput;
16
17/// Makes a native Rust function usable as a Typst function.
18///
19/// This implements `NativeFunction` for a freshly generated type with the same
20/// name as a function. (In Rust, functions and types live in separate
21/// namespace, so both can coexist.)
22///
23/// If the function is in an impl block annotated with `#[scope]`, things work a
24/// bit differently because the no type can be generated within the impl block.
25/// In that case, a function named `{name}_data` that returns `&'static
26/// NativeFuncData` is generated. You typically don't need to interact with this
27/// function though because the `#[scope]` macro hooks everything up for you.
28///
29/// ```ignore
30/// /// Doubles an integer.
31/// #[func]
32/// fn double(x: i64) -> i64 {
33///     2 * x
34/// }
35/// ```
36///
37/// # Properties
38/// You can customize some properties of the resulting function:
39/// - `scope`: Indicates that the function has an associated scope defined by
40///   the `#[scope]` macro.
41/// - `contextual`: Indicates that the function makes use of context. This has
42///   no effect on the behaviour itself, but is used for the docs.
43/// - `name`: The functions's normal name (e.g. `min`), as exposed to Typst.
44///   Defaults to the Rust name in kebab-case.
45/// - `title`: The functions's title case name (e.g. `Minimum`). Defaults to the
46///   normal name in title case.
47/// - `keywords = [..]`: A list of alternate search terms for this function.
48/// - `constructor`: Indicates that the function is a constructor.
49///
50/// # Arguments
51/// By default, function arguments are positional and required. You can use
52/// various attributes to configure their parsing behaviour:
53///
54/// - `#[named]`: Makes the argument named and optional. The argument type must
55///   either be `Option<_>` _or_ the `#[default]` attribute must be used. (If
56///   it's both `Option<_>` and `#[default]`, then the argument can be specified
57///   as `none` in Typst).
58/// - `#[default]`: Specifies the default value of the argument as
59///   `Default::default()`.
60/// - `#[default(..)]`: Specifies the default value of the argument as `..`.
61/// - `#[variadic]`: Parses a variable number of arguments. The argument type
62///   must be `Vec<_>`.
63/// - `#[external]`: The argument appears in documentation, but is otherwise
64///   ignored. Can be useful if you want to do something manually for more
65///   flexibility.
66///
67/// Defaults can be specified for positional and named arguments. This is in
68/// contrast to user-defined functions which currently cannot have optional
69/// positional arguments (except through argument sinks).
70///
71/// In the example below, we define a `min` function that could be called as
72/// `min(1, 2, 3, default: 0)` in Typst.
73///
74/// ```ignore
75/// /// Determines the minimum of a sequence of values.
76/// #[func(title = "Minimum")]
77/// fn min(
78///     /// The values to extract the minimum from.
79///     #[variadic]
80///     values: Vec<i64>,
81///     /// A default value to return if there are no values.
82///     #[named]
83///     #[default(0)]
84///     default: i64,
85/// ) -> i64 {
86///     self.values.iter().min().unwrap_or(default)
87/// }
88/// ```
89///
90/// As you can see, arguments can also have doc-comments, which will be rendered
91/// in the documentation. The first line of documentation should be concise and
92/// self-contained as it is the designated short description, which is used in
93/// overviews in the documentation (and for autocompletion).
94///
95/// Additionally, some arguments are treated specially by this macro:
96///
97/// - `engine`: The compilation context (`Engine`).
98/// - `context`: The introspection context (`Tracked<Context>`).
99/// - `args`: The rest of the arguments passed into this function (`&mut Args`).
100/// - `span`: The span of the function call (`Span`).
101///
102/// These should always come after `self`, in the order specified.
103#[proc_macro_attribute]
104pub fn func(stream: BoundaryStream, item: BoundaryStream) -> BoundaryStream {
105    let item = syn::parse_macro_input!(item as syn::ItemFn);
106    func::func(stream.into(), &item)
107        .unwrap_or_else(|err| err.to_compile_error())
108        .into()
109}
110
111/// Makes a native Rust type usable as a Typst type.
112///
113/// This implements `NativeType` for the given type.
114///
115/// ```ignore
116/// /// A sequence of codepoints.
117/// #[ty(scope, title = "String")]
118/// struct Str(EcoString);
119///
120/// #[scope]
121/// impl Str {
122///     ...
123/// }
124/// ```
125///
126/// # Properties
127/// You can customize some properties of the resulting type:
128/// - `scope`: Indicates that the type has an associated scope defined by the
129///   `#[scope]` macro
130/// - `cast`: Indicates that the type has a custom `cast!` implementation.
131///   The macro will then not autogenerate one.
132/// - `name`: The type's normal name (e.g. `str`), as exposed to Typst.
133///   Defaults to the Rust name in kebab-case.
134/// - `title`: The type's title case name (e.g. `String`). Defaults to the
135///   normal name in title case.
136#[proc_macro_attribute]
137pub fn ty(stream: BoundaryStream, item: BoundaryStream) -> BoundaryStream {
138    let item = syn::parse_macro_input!(item as syn::Item);
139    ty::ty(stream.into(), item)
140        .unwrap_or_else(|err| err.to_compile_error())
141        .into()
142}
143
144/// Makes a native Rust type usable as a Typst element.
145///
146/// This implements `NativeElement` for the given type.
147///
148/// ```ignore
149/// /// A section heading.
150/// #[elem(Show, Count)]
151/// struct HeadingElem {
152///     /// The logical nesting depth of the heading, starting from one.
153///     #[default(NonZeroUsize::ONE)]
154///     level: NonZeroUsize,
155///
156///     /// The heading's title.
157///     #[required]
158///     body: Content,
159/// }
160/// ```
161///
162/// # Properties
163/// You can customize some properties of the resulting type:
164/// - `scope`: Indicates that the type has an associated scope defined by the
165///   `#[scope]` macro.
166/// - `name = "<name>"`: The element's normal name (e.g. `align`), as exposed to Typst.
167///   Defaults to the Rust name in kebab-case.
168/// - `title = "<title>"`: The type's title case name (e.g. `Align`). Defaults to the long
169///   name in title case.
170/// - `keywords = [..]`: A list of alternate search terms for this element.
171///   Defaults to the empty list.
172/// - The remaining entries in the `elem` macros list are traits the element
173///   is capable of. These can be dynamically accessed.
174///
175/// # Fields
176/// By default, element fields are named and optional (and thus settable). You
177/// can use various attributes to configure their parsing behaviour:
178///
179/// - `#[positional]`: Makes the argument positional (but still optional).
180/// - `#[required]`: Makes the argument positional and required.
181/// - `#[default(..)]`: Specifies the default value of the argument as `..`.
182/// - `#[variadic]`: Parses a variable number of arguments. The field type must
183///   be `Vec<_>`. The field will be exposed as an array.
184/// - `#[parse({ .. })]`: A block of code that parses the field manually.
185///
186/// In addition that there are a number of attributes that configure other
187/// aspects of the field than the parsing behaviour.
188/// - `#[resolve]`: When accessing the field, it will be automatically
189///   resolved through the `Resolve` trait. This, for instance, turns `Length`
190///   into `Abs`. It's just convenient.
191/// - `#[fold]`: When there are multiple set rules for the field, all values
192///   are folded together into one. E.g. `set rect(stroke: 2pt)` and
193///   `set rect(stroke: red)` are combined into the equivalent of
194///   `set rect(stroke: 2pt + red)` instead of having `red` override `2pt`.
195/// - `#[borrowed]`: For fields that are accessed through the style chain,
196///   indicates that accessor methods to this field should return references
197///   to the value instead of cloning.
198/// - `#[internal]`: The field does not appear in the documentation.
199/// - `#[external]`: The field appears in the documentation, but is otherwise
200///   ignored. Can be useful if you want to do something manually for more
201///   flexibility.
202/// - `#[synthesized]`: The field cannot be specified in a constructor or set
203///   rule. Instead, it is added to an element before its show rule runs
204///   through the `Synthesize` trait.
205/// - `#[ghost]`: Allows creating fields that are only present in the style chain,
206///   this means that they *cannot* be accessed by the user, they cannot be set
207///   on an individual instantiated element, and must be set via the style chain.
208///   This is useful for fields that are only used internally by the style chain,
209///   such as the fields from `ParElem` and `TextElem`. If your element contains
210///   any ghost fields, then you cannot auto-generate `Construct` for it, and
211///   you must implement `Construct` manually.
212#[proc_macro_attribute]
213pub fn elem(stream: BoundaryStream, item: BoundaryStream) -> BoundaryStream {
214    let item = syn::parse_macro_input!(item as syn::ItemStruct);
215    elem::elem(stream.into(), item)
216        .unwrap_or_else(|err| err.to_compile_error())
217        .into()
218}
219
220/// Provides an associated scope to a native function, type, or element.
221///
222/// This implements `NativeScope` for the function's shadow type, the type, or
223/// the element.
224///
225/// The implementation block can contain four kinds of items:
226/// - constants, which will be defined through `scope.define`
227/// - functions, which will be defined through `scope.define_func`
228/// - types, which will be defined through `scope.define_type`
229/// - elements, which will be defined through `scope.define_elem`
230///
231/// ```ignore
232/// #[func(scope)]
233/// fn name() { .. }
234///
235/// #[scope]
236/// impl name {
237///     /// A simple constant.
238///     const VAL: u32 = 0;
239///
240///     /// A function.
241///     #[func]
242///     fn foo() -> EcoString {
243///         "foo!".into()
244///     }
245///
246///     /// A type.
247///     type Brr;
248///
249///     /// An element.
250///     #[elem]
251///     type NiceElem;
252/// }
253///
254/// #[ty]
255/// struct Brr;
256///
257/// #[elem]
258/// struct NiceElem {}
259/// ```
260#[proc_macro_attribute]
261pub fn scope(stream: BoundaryStream, item: BoundaryStream) -> BoundaryStream {
262    let item = syn::parse_macro_input!(item as syn::Item);
263    scope::scope(stream.into(), item)
264        .unwrap_or_else(|err| err.to_compile_error())
265        .into()
266}
267
268/// Implements `Reflect`, `FromValue`, and `IntoValue` for a type.
269///
270/// - `Reflect` makes Typst's runtime aware of the type's characteristics.
271///   It's important for autocompletion, error messages, etc.
272/// - `FromValue` defines how to cast from a value into this type.
273/// - `IntoValue` defines how to cast from this type into a value.
274///
275/// ```ignore
276/// /// An integer between 0 and 13.
277/// struct CoolInt(u8);
278///
279/// cast! {
280///     CoolInt,
281///
282///     // Defines how to turn a `CoolInt` into a value.
283///     self => self.0.into_value(),
284///
285///     // Defines "match arms" of types that can be cast into a `CoolInt`.
286///     // These types needn't be value primitives, they can themselves use
287///     // `cast!`.
288///     v: bool => Self(v as u8),
289///     v: i64 => if matches!(v, 0..=13) {
290///         Self(v as u8)
291///     } else {
292///         bail!("integer is not nice :/")
293///     },
294/// }
295/// ```
296#[proc_macro]
297pub fn cast(stream: BoundaryStream) -> BoundaryStream {
298    cast::cast(stream.into())
299        .unwrap_or_else(|err| err.to_compile_error())
300        .into()
301}
302
303/// Implements `Reflect`, `FromValue`, and `IntoValue` for an enum.
304///
305/// The enum will become castable from kebab-case strings. The doc-comments will
306/// become user-facing documentation for each variant. The `#[string]` attribute
307/// can be used to override the string corresponding to a variant.
308///
309/// ```ignore
310/// /// A stringy enum of options.
311/// #[derive(Cast)]
312/// enum Niceness {
313///     /// Clearly nice (parses from `"nice"`).
314///     Nice,
315///     /// Not so nice (parses from `"not-nice"`).
316///     NotNice,
317///     /// Very much not nice (parses from `"❌"`).
318///     #[string("❌")]
319///     Unnice,
320/// }
321/// ```
322#[proc_macro_derive(Cast, attributes(string))]
323pub fn derive_cast(item: BoundaryStream) -> BoundaryStream {
324    let item = syn::parse_macro_input!(item as DeriveInput);
325    cast::derive_cast(item)
326        .unwrap_or_else(|err| err.to_compile_error())
327        .into()
328}
329
330/// Times function invocations.
331///
332/// When tracing is enabled in the typst-cli, this macro will record the
333/// invocations of the function and store them in a global map. The map can be
334/// accessed through the `typst_trace::RECORDER` static.
335///
336/// You can also specify the span of the function invocation:
337/// - `#[time(span = ..)]` to record the span, which will be used for the
338///   `EventKey`.
339///
340/// By default, all tracing is omitted using the `wasm32` target flag.
341/// This is done to avoid bloating the web app, which doesn't need tracing.
342///
343/// ```ignore
344/// #[time]
345/// fn fibonacci(n: u64) -> u64 {
346///     if n <= 1 {
347///         1
348///     } else {
349///         fibonacci(n - 1) + fibonacci(n - 2)
350///     }
351/// }
352///
353/// #[time(span = span)]
354/// fn fibonacci_spanned(n: u64, span: Span) -> u64 {
355///     if n <= 1 {
356///         1
357///     } else {
358///         fibonacci(n - 1) + fibonacci(n - 2)
359///     }
360/// }
361/// ```
362#[proc_macro_attribute]
363pub fn time(stream: BoundaryStream, item: BoundaryStream) -> BoundaryStream {
364    let item = syn::parse_macro_input!(item as syn::ItemFn);
365    time::time(stream.into(), item)
366        .unwrap_or_else(|err| err.to_compile_error())
367        .into()
368}