mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
168 lines
5.6 KiB
Rust
168 lines
5.6 KiB
Rust
//! Helper types and macros for creating custom functions.
|
|
|
|
/// Defines function types concisely.
|
|
#[macro_export]
|
|
macro_rules! function {
|
|
// Parse a unit struct.
|
|
($(#[$outer:meta])* pub struct $type:ident; $($rest:tt)*) => {
|
|
$(#[$outer])* pub struct $type;
|
|
function!(@meta $type | $($rest)*);
|
|
};
|
|
|
|
// Parse a tuple struct.
|
|
($(#[$outer:meta])* pub struct $type:ident($($fields:tt)*); $($rest:tt)*) => {
|
|
$(#[$outer])* pub struct $type($($fields)*);
|
|
function!(@meta $type | $($rest)*);
|
|
};
|
|
|
|
// Parse a struct with fields.
|
|
($(#[$outer:meta])* pub struct $type:ident { $($fields:tt)* } $($rest:tt)*) => {
|
|
$(#[$outer])* pub struct $type { $($fields)* }
|
|
function!(@meta $type | $($rest)*);
|
|
};
|
|
|
|
// Parse an enum.
|
|
($(#[$outer:meta])* pub enum $type:ident { $($fields:tt)* } $($rest:tt)*) => {
|
|
$(#[$outer])* pub enum $type { $($fields)* }
|
|
function!(@meta $type | $($rest)*);
|
|
};
|
|
|
|
// Parse a metadata type definition.
|
|
(@meta $type:ident | type Meta = $meta:ty; $($rest:tt)*) => {
|
|
function!(@parse $type $meta | $($rest)*);
|
|
};
|
|
|
|
// Set the metadata to `()` if there is no type definition.
|
|
(@meta $type:ident | $($rest:tt)*) => {
|
|
function!(@parse $type () | $($rest)*);
|
|
};
|
|
|
|
// Parse a `parse(default)`.
|
|
(@parse $type:ident $meta:ty | parse(default) $($rest:tt)*) => {
|
|
function!(@parse $type $meta |
|
|
parse(_args, _body, _ctx, _meta) { Default::default() }
|
|
$($rest)*
|
|
);
|
|
};
|
|
|
|
// (0-arg) Parse a parse-definition without arguments.
|
|
(@parse $type:ident $meta:ty | parse() $code:block $($rest:tt)*) => {
|
|
function!(@parse $type $meta | parse(_args, _body, _ctx, _meta) $code $($rest)*);
|
|
};
|
|
|
|
// (1-arg) Parse a parse-definition with only the first argument.
|
|
(@parse $type:ident $meta:ty | parse($args:ident) $code:block $($rest:tt)*) => {
|
|
function!(@parse $type $meta | parse($args, _body, _ctx, _meta) $code $($rest)*);
|
|
};
|
|
|
|
// (2-arg) Parse a parse-definition with only the first two arguments.
|
|
(@parse $type:ident $meta:ty |
|
|
parse($args:ident, $body:pat) $code:block $($rest:tt)*
|
|
) => {
|
|
function!(@parse $type $meta | parse($args, $body, _ctx, _meta) $code $($rest)*);
|
|
};
|
|
|
|
// (3-arg) Parse a parse-definition with only the first three arguments.
|
|
(@parse $type:ident $meta:ty |
|
|
parse($args:ident, $body:pat, $ctx:pat) $code:block $($rest:tt)*
|
|
) => {
|
|
function!(@parse $type $meta | parse($args, $body, $ctx, _meta) $code $($rest)*);
|
|
};
|
|
|
|
// (4-arg) Parse a parse-definition with all four arguments.
|
|
(@parse $type:ident $meta:ty |
|
|
parse($args:ident, $body:pat, $ctx:pat, $metadata:pat) $code:block
|
|
$($rest:tt)*
|
|
) => {
|
|
impl $crate::func::ParseFunc for $type {
|
|
type Meta = $meta;
|
|
|
|
fn parse(
|
|
args: FuncArgs,
|
|
$body: Option<Spanned<&str>>,
|
|
$ctx: ParseContext,
|
|
$metadata: Self::Meta,
|
|
) -> ParseResult<Self> where Self: Sized {
|
|
#[allow(unused_mut)]
|
|
let mut $args = args;
|
|
let val = $code;
|
|
if !$args.is_empty() {
|
|
error!(unexpected_argument);
|
|
}
|
|
Ok(val)
|
|
}
|
|
}
|
|
|
|
function!(@layout $type | $($rest)*);
|
|
};
|
|
|
|
// (0-arg) Parse a layout-definition without arguments.
|
|
(@layout $type:ident | layout() $code:block) => {
|
|
function!(@layout $type | layout(self, _ctx) $code);
|
|
};
|
|
|
|
// (1-arg) Parse a layout-definition with only the first argument.
|
|
(@layout $type:ident | layout($this:ident) $code:block) => {
|
|
function!(@layout $type | layout($this, _ctx) $code);
|
|
};
|
|
|
|
// (2-arg) Parse a layout-definition with all arguments.
|
|
(@layout $type:ident | layout($this:ident, $ctx:pat) $code:block) => {
|
|
impl LayoutFunc for $type {
|
|
fn layout<'a, 'life0, 'life1, 'async_trait>(
|
|
&'a $this,
|
|
$ctx: LayoutContext<'life0, 'life1>
|
|
) -> std::pin::Pin<Box<
|
|
dyn std::future::Future<Output = LayoutResult<Commands<'a>>> + 'async_trait
|
|
>>
|
|
where
|
|
'a: 'async_trait,
|
|
'life0: 'async_trait,
|
|
'life1: 'async_trait,
|
|
Self: 'async_trait,
|
|
{
|
|
Box::pin(async move { Ok($code) })
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
/// Parse the body of a function.
|
|
///
|
|
/// - If the function does not expect a body, use `parse!(forbidden: body)`.
|
|
/// - If the function can have a body, use `parse!(optional: body, ctx)`.
|
|
/// - If the function must have a body, use `parse!(expected: body, ctx)`.
|
|
#[macro_export]
|
|
macro_rules! parse {
|
|
(forbidden: $body:expr) => {
|
|
if $body.is_some() {
|
|
error!("unexpected body");
|
|
}
|
|
};
|
|
|
|
(optional: $body:expr, $ctx:expr) => (
|
|
if let Some(body) = $body {
|
|
Some($crate::syntax::parse(body.v, $ctx)?)
|
|
} else {
|
|
None
|
|
}
|
|
);
|
|
|
|
(expected: $body:expr, $ctx:expr) => (
|
|
if let Some(body) = $body {
|
|
$crate::syntax::parse(body.v, $ctx)?
|
|
} else {
|
|
error!("expected body");
|
|
}
|
|
)
|
|
}
|
|
|
|
/// Early-return with a formatted typesetting error or construct an error
|
|
/// expression.
|
|
#[macro_export]
|
|
macro_rules! error {
|
|
(@unexpected_argument) => (error!(@"unexpected argument"));
|
|
(@$($tts:tt)*) => ($crate::TypesetError::with_message(format!($($tts)*)));
|
|
($($tts:tt)*) => (return Err(error!(@$($tts)*)););
|
|
}
|