Skip to main content

typst_library/foundations/
func.rs

1#[doc(inline)]
2pub use typst_macros::func;
3use typst_syntax::ast::AstNode;
4
5use std::fmt::{self, Debug, Formatter};
6use std::sync::{Arc, LazyLock};
7
8use comemo::{Tracked, TrackedMut};
9use ecow::{EcoString, eco_format};
10use either::Either;
11use typst_syntax::{Span, Spanned, SyntaxNode, ast};
12use typst_utils::{DefSite, LazyHash, Static, singleton};
13
14use crate::diag::{At, SourceResult, StrResult, WarningSink, bail};
15use crate::engine::Engine;
16use crate::foundations::{
17    Args, Bytes, CastInfo, Content, Context, Element, IntoArgs, PluginFunc, Repr, Scope,
18    Selector, Type, Value, cast, scope, ty,
19};
20
21/// A mapping from argument values to a return value.
22///
23/// You can call a function by writing a comma-separated list of function
24/// _arguments_ enclosed in parentheses directly after the function name.
25/// Additionally, you can pass any number of trailing content block arguments to
26/// a function _after_ the normal argument list. If the normal argument list
27/// would become empty, it can be omitted. Typst supports positional and named
28/// arguments. The former are identified by position and type, while the latter
29/// are written as `name: value`.
30///
31/// Within math mode, function calls have special behaviour. See the
32/// @math[math documentation] for more details.
33///
34/// = Example <example>
35/// ```example
36/// // Call a function.
37/// #list([A], [B])
38///
39/// // Named arguments and trailing
40/// // content blocks.
41/// #enum(start: 2)[A][B]
42///
43/// // Version without parentheses.
44/// #list[A][B]
45/// ```
46///
47/// Functions are a fundamental building block of Typst. Typst provides
48/// functions for a variety of typesetting tasks. Moreover, the markup you write
49/// is backed by functions and all styling happens through functions. This
50/// reference lists all available functions and how you can use them. Please
51/// also refer to the documentation about @reference:styling:set-rules[set] and
52/// @reference:styling:show-rules[show] rules to learn about additional ways you
53/// can work with functions in Typst.
54///
55/// = Element functions <element-functions>
56/// Some functions are associated with _elements_ like @heading[headings] or
57/// @table[tables]. When called, these create an element of their respective
58/// kind. In contrast to normal functions, they can further be used in
59/// @reference:styling:set-rules[set rules],
60/// @reference:styling:show-rules[show rules], and @selector[selectors].
61///
62/// = Function scopes <function-scopes>
63/// Functions can hold related definitions in their own scope, similar to a
64/// @reference:scripting:modules[module]. Examples of this are @assert.eq or
65/// @list.item. However, this feature is currently only available for built-in
66/// functions.
67///
68/// = Defining functions <defining-functions>
69/// You can define your own function with a
70/// @reference:scripting:bindings[let binding] that has a parameter list after
71/// the binding's name. The parameter list can contain mandatory positional
72/// parameters, named parameters with default values and
73/// @arguments[argument sinks].
74///
75/// The right-hand side of a function binding is the function body, which can be
76/// a block or any other expression. It defines the function's return value and
77/// can depend on the parameters. If the function body is a
78/// @reference:scripting:blocks[code block], the return value is the result of
79/// joining the values of each expression in the block.
80///
81/// Within a function body, the `return` keyword can be used to exit early and
82/// optionally specify a return value. If no explicit return value is given, the
83/// body evaluates to the result of joining all expressions preceding the
84/// `return`.
85///
86/// Functions that don't return any meaningful value return @none instead. The
87/// return type of such functions is not explicitly specified in the
88/// documentation. (An example of this is @array.push).
89///
90/// ```example
91/// #let alert(body, fill: red) = {
92///   set text(white)
93///   set align(center)
94///   rect(
95///     fill: fill,
96///     inset: 8pt,
97///     radius: 4pt,
98///     [*Warning:\ #body*],
99///   )
100/// }
101///
102/// #alert[
103///   Danger is imminent!
104/// ]
105///
106/// #alert(fill: blue)[
107///   KEEP OFF TRACKS
108/// ]
109/// ```
110///
111/// = Importing functions <importing-functions>
112/// Functions can be imported from one file
113/// (@reference:scripting:modules[`module`]) into another using `{import}`. For
114/// example, assume that we have defined the `alert` function from the previous
115/// example in a file called `foo.typ`. We can import it into another file by
116/// writing `{import "foo.typ": alert}`.
117///
118/// = #short-or-long[Unnamed][Unnamed functions] <unnamed>
119/// You can also create an unnamed function without creating a binding by
120/// specifying a parameter list followed by `=>` and the function body. If your
121/// function has just one parameter, the parentheses around the parameter list
122/// are optional. Unnamed functions are mainly useful for show rules, but also
123/// as arguments to other functions, like @array.position.
124///
125/// ```example
126/// #show "once?": it => [#it #it]
127/// once?
128/// ```
129///
130/// = Note on function purity <note-on-function-purity>
131/// In Typst, all functions are _pure._ This means that for the same arguments,
132/// they always return the same result. They cannot "remember" things to produce
133/// another value when they are called a second time.
134///
135/// The only exception are built-in methods like
136/// @array.push[`array.push(value)`]. These can modify the values they are
137/// called on.
138#[ty(scope, cast, name = "function")]
139#[derive(Clone, Hash)]
140pub struct Func {
141    /// The internal representation.
142    inner: FuncInner,
143    /// The span with which errors are reported when this function is called.
144    span: Span,
145}
146
147/// The different kinds of function representations.
148#[derive(Clone, PartialEq, Hash)]
149enum FuncInner {
150    /// A native Rust function.
151    Native(Static<NativeFuncData>),
152    /// A function for an element.
153    Element(Element),
154    /// A user-defined closure.
155    Closure(Arc<LazyHash<Closure>>),
156    /// A plugin WebAssembly function.
157    Plugin(Arc<PluginFunc>),
158    /// A nested function with pre-applied arguments.
159    With(Arc<(Func, Args)>),
160}
161
162impl Func {
163    /// The function's name (e.g. `min`).
164    ///
165    /// Returns `None` if this is an anonymous closure.
166    pub fn name(&self) -> Option<&str> {
167        match &self.inner {
168            FuncInner::Native(native) => Some(native.name),
169            FuncInner::Element(elem) => Some(elem.name()),
170            FuncInner::Closure(closure) => closure.name(),
171            FuncInner::Plugin(func) => Some(func.name()),
172            FuncInner::With(with) => with.0.name(),
173        }
174    }
175
176    /// The function's title case name, for use in documentation (e.g. `Minimum`).
177    ///
178    /// Returns `None` if this is a closure.
179    pub fn title(&self) -> Option<&'static str> {
180        match &self.inner {
181            FuncInner::Native(native) => Some(native.title),
182            FuncInner::Element(elem) => Some(elem.title()),
183            FuncInner::Closure(_) => None,
184            FuncInner::Plugin(_) => None,
185            FuncInner::With(with) => with.0.title(),
186        }
187    }
188
189    /// Documentation for the function (as Markdown).
190    pub fn docs(&self) -> Option<&'static str> {
191        match &self.inner {
192            FuncInner::Native(native) => Some(native.docs),
193            FuncInner::Element(elem) => Some(elem.docs()),
194            FuncInner::Closure(_) => None,
195            FuncInner::Plugin(_) => None,
196            FuncInner::With(with) => with.0.docs(),
197        }
198    }
199
200    /// Whether the function is known to be contextual.
201    pub fn contextual(&self) -> Option<bool> {
202        match &self.inner {
203            FuncInner::Native(native) => Some(native.contextual),
204            _ => None,
205        }
206    }
207
208    /// Get details about this function's parameters if available.
209    pub fn params(&self) -> impl Iterator<Item = ParamInfo> {
210        match &self.inner {
211            FuncInner::Native(native) => {
212                Either::Left(native.0.params.iter().map(ParamInfo::Native))
213            }
214            FuncInner::Element(elem) => {
215                Either::Left(elem.params().iter().map(ParamInfo::Native))
216            }
217            FuncInner::Closure(closure) => {
218                Either::Right(Either::Left(closure.params().map(ParamInfo::Closure)))
219            }
220            FuncInner::Plugin(_) => {
221                Either::Right(Either::Right([ParamInfo::Plugin].into_iter()))
222            }
223            // TODO: We could take into account the known arguments.
224            FuncInner::With(with) => with.0.params(),
225        }
226    }
227
228    /// Get the parameter info for a parameter with the given name if it exist.
229    pub fn param(&self, name: &str) -> Option<ParamInfo> {
230        self.params().find(|param| param.name() == Some(name))
231    }
232
233    /// Get details about the function's return type.
234    pub fn returns(&self) -> Option<&'static CastInfo> {
235        match &self.inner {
236            FuncInner::Native(native) => Some(&native.0.returns),
237            FuncInner::Element(_) => {
238                Some(singleton!(CastInfo, CastInfo::Type(Type::of::<Content>())))
239            }
240            FuncInner::Closure(_) => None,
241            FuncInner::Plugin(_) => None,
242            FuncInner::With(with) => with.0.returns(),
243        }
244    }
245
246    /// Search keywords for the function.
247    pub fn keywords(&self) -> &'static [&'static str] {
248        match &self.inner {
249            FuncInner::Native(native) => native.keywords,
250            FuncInner::Element(elem) => elem.keywords(),
251            FuncInner::Closure(_) => &[],
252            FuncInner::Plugin(_) => &[],
253            FuncInner::With(with) => with.0.keywords(),
254        }
255    }
256
257    /// Where the function is defined in the Rust source code (only `Some(_)` if
258    /// it is native, and even then it can be `None` if the function is
259    /// generated, like the typed HTML API).
260    pub fn def_site(&self) -> Option<DefSite> {
261        match &self.inner {
262            FuncInner::Native(native) => native.def_site,
263            FuncInner::Element(elem) => Some(elem.def_site()),
264            _ => None,
265        }
266    }
267
268    /// The function's associated scope of sub-definition.
269    pub fn scope(&self) -> Option<&'static Scope> {
270        match &self.inner {
271            FuncInner::Native(native) => Some(&native.0.scope),
272            FuncInner::Element(elem) => Some(elem.scope()),
273            FuncInner::Closure(_) => None,
274            FuncInner::Plugin(_) => None,
275            FuncInner::With(with) => with.0.scope(),
276        }
277    }
278
279    /// Get a field from this function's scope, if possible.
280    pub fn field(
281        &self,
282        field: &str,
283        sink: impl WarningSink,
284    ) -> StrResult<&'static Value> {
285        let scope =
286            self.scope().ok_or("cannot access fields on user-defined functions")?;
287        match scope.get(field) {
288            Some(binding) => Ok(binding.read_checked(sink)),
289            None => match self.name() {
290                Some(name) => bail!("function `{name}` does not contain field `{field}`"),
291                None => bail!("function does not contain field `{field}`"),
292            },
293        }
294    }
295
296    /// Extract the element function, if it is one.
297    pub fn to_element(&self) -> Option<Element> {
298        match self.inner {
299            FuncInner::Element(func) => Some(func),
300            _ => None,
301        }
302    }
303
304    /// Extract the plugin function, if it is one.
305    pub fn to_plugin(&self) -> Option<&PluginFunc> {
306        match &self.inner {
307            FuncInner::Plugin(func) => Some(func),
308            _ => None,
309        }
310    }
311
312    /// Call the function with the given context and arguments.
313    pub fn call<A: IntoArgs>(
314        &self,
315        engine: &mut Engine,
316        context: Tracked<Context>,
317        args: A,
318    ) -> SourceResult<Value> {
319        self.call_impl(engine, context, args.into_args(self.span))
320    }
321
322    /// Non-generic implementation of `call`.
323    #[typst_macros::time(name = "func call", span = self.span())]
324    fn call_impl(
325        &self,
326        engine: &mut Engine,
327        context: Tracked<Context>,
328        mut args: Args,
329    ) -> SourceResult<Value> {
330        match &self.inner {
331            FuncInner::Native(native) => {
332                let value = (native.function.0)(engine, context, &mut args)?;
333                args.finish()?;
334                Ok(value)
335            }
336            FuncInner::Element(func) => {
337                let value = func.construct(engine, &mut args)?;
338                args.finish()?;
339                Ok(Value::Content(value))
340            }
341            FuncInner::Closure(closure) => (engine.library.routines.eval_closure)(
342                self,
343                closure,
344                engine.world,
345                engine.library,
346                engine.introspector.into_raw(),
347                engine.traced,
348                TrackedMut::reborrow_mut(&mut engine.sink),
349                engine.route.track(),
350                context,
351                args,
352            ),
353            FuncInner::Plugin(func) => {
354                let inputs = args.all::<Bytes>()?;
355                let output = func.call(inputs).at(args.span)?;
356                args.finish()?;
357                Ok(Value::Bytes(output))
358            }
359            FuncInner::With(with) => {
360                args.items = with.1.items.iter().cloned().chain(args.items).collect();
361                with.0.call(engine, context, args)
362            }
363        }
364    }
365
366    /// The function's span.
367    pub fn span(&self) -> Span {
368        self.span
369    }
370
371    /// Attach a span to this function if it doesn't already have one.
372    pub fn spanned(mut self, span: Span) -> Self {
373        if self.span.is_detached() {
374            self.span = span;
375        }
376        self
377    }
378}
379
380#[scope]
381impl Func {
382    /// Returns a new function that has the given arguments pre-applied.
383    #[func]
384    pub fn with(
385        self,
386        args: &mut Args,
387        /// The arguments to apply to the function.
388        #[external]
389        #[variadic]
390        arguments: Vec<Value>,
391    ) -> Func {
392        let span = self.span;
393        Self {
394            inner: FuncInner::With(Arc::new((self, args.take()))),
395            span,
396        }
397    }
398
399    /// Returns a selector that filters for elements belonging to this function
400    /// whose fields have the values of the given arguments.
401    ///
402    /// ```example
403    /// #show heading.where(level: 2): set text(blue)
404    /// = Section
405    /// == Subsection
406    /// === Sub-subsection
407    /// ```
408    #[func]
409    pub fn where_(
410        self,
411        args: &mut Args,
412        /// The fields to filter for.
413        #[variadic]
414        #[external]
415        fields: Vec<Value>,
416    ) -> StrResult<Selector> {
417        let fields = args.to_named();
418        args.items.retain(|arg| arg.name.is_none());
419
420        let element = self
421            .to_element()
422            .ok_or("`where()` can only be called on element functions")?;
423
424        let fields = fields
425            .into_iter()
426            .map(|(key, value)| {
427                element.field_id(&key).map(|id| (id, value)).ok_or_else(|| {
428                    eco_format!(
429                        "element `{}` does not have field `{}`",
430                        element.name(),
431                        key
432                    )
433                })
434            })
435            .collect::<StrResult<smallvec::SmallVec<_>>>()?;
436
437        Ok(element.where_(fields))
438    }
439}
440
441impl Debug for Func {
442    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
443        write!(f, "Func({})", self.name().unwrap_or(".."))
444    }
445}
446
447impl Repr for Func {
448    fn repr(&self) -> EcoString {
449        const DEFAULT: &str = "(..) => ..";
450        match &self.inner {
451            FuncInner::Native(native) => native.name.into(),
452            FuncInner::Element(elem) => elem.name().into(),
453            FuncInner::Closure(closure) => closure.name().unwrap_or(DEFAULT).into(),
454            FuncInner::Plugin(func) => func.name().clone(),
455            FuncInner::With(_) => DEFAULT.into(),
456        }
457    }
458}
459
460impl PartialEq for Func {
461    fn eq(&self, other: &Self) -> bool {
462        self.inner == other.inner
463    }
464}
465
466impl PartialEq<&'static NativeFuncData> for Func {
467    fn eq(&self, other: &&'static NativeFuncData) -> bool {
468        match &self.inner {
469            FuncInner::Native(native) => *native == Static(*other),
470            _ => false,
471        }
472    }
473}
474
475impl PartialEq<Element> for Func {
476    fn eq(&self, other: &Element) -> bool {
477        match &self.inner {
478            FuncInner::Element(elem) => elem == other,
479            _ => false,
480        }
481    }
482}
483
484impl From<FuncInner> for Func {
485    fn from(inner: FuncInner) -> Self {
486        Self { inner, span: Span::detached() }
487    }
488}
489
490impl From<&'static NativeFuncData> for Func {
491    fn from(data: &'static NativeFuncData) -> Self {
492        FuncInner::Native(Static(data)).into()
493    }
494}
495
496impl From<Element> for Func {
497    fn from(func: Element) -> Self {
498        FuncInner::Element(func).into()
499    }
500}
501
502impl From<Closure> for Func {
503    fn from(closure: Closure) -> Self {
504        FuncInner::Closure(Arc::new(LazyHash::new(closure))).into()
505    }
506}
507
508impl From<PluginFunc> for Func {
509    fn from(func: PluginFunc) -> Self {
510        FuncInner::Plugin(Arc::new(func)).into()
511    }
512}
513
514/// Details about a function parameter.
515#[derive(Debug, Clone)]
516pub enum ParamInfo {
517    /// Details about a parameter of a native, Rust-defined function.
518    Native(&'static NativeParamInfo),
519    /// Details about a user-defined function.
520    Closure(Spanned<ClosureParamInfo>),
521    /// A plugin's sole variadic bytes parameter.
522    Plugin,
523}
524
525impl ParamInfo {
526    /// Attempts to cast to a native parameter info.
527    pub fn to_native(&self) -> Option<&'static NativeParamInfo> {
528        match self {
529            Self::Native(native) => Some(native),
530            _ => None,
531        }
532    }
533
534    /// The parameter's name, if any.
535    pub fn name(&self) -> Option<&str> {
536        match self {
537            Self::Native(info) => Some(info.name),
538            Self::Closure(info) => match &info.v {
539                ClosureParamInfo::Pos { name } => name.as_deref(),
540                ClosureParamInfo::Sink { name } => name.as_deref(),
541                ClosureParamInfo::Named { name, .. } => Some(name),
542            },
543            Self::Plugin => None,
544        }
545    }
546
547    /// The parameter's default value, if any.
548    pub fn default(&self) -> Option<Value> {
549        match self {
550            Self::Native(info) => info.default.map(|f| f()),
551            Self::Closure(info) => match &info.v {
552                ClosureParamInfo::Named { default, .. } => Some(default.clone()),
553                _ => None,
554            },
555            Self::Plugin => None,
556        }
557    }
558
559    /// Whether the parameter is positional (includes variadic).
560    pub fn positional(&self) -> bool {
561        match self {
562            Self::Native(info) => info.positional,
563            Self::Closure(info) => matches!(
564                &info.v,
565                ClosureParamInfo::Pos { .. } | ClosureParamInfo::Sink { .. }
566            ),
567            Self::Plugin => true,
568        }
569    }
570
571    /// Whether the parameter is named.
572    pub fn named(&self) -> bool {
573        match self {
574            Self::Native(info) => info.named,
575            Self::Closure(info) => matches!(&info.v, ClosureParamInfo::Named { .. }),
576            Self::Plugin => false,
577        }
578    }
579
580    /// Whether the parameter is variadic.
581    pub fn variadic(&self) -> bool {
582        match self {
583            Self::Native(info) => info.variadic,
584            Self::Closure(info) => matches!(&info.v, ClosureParamInfo::Sink { .. }),
585            Self::Plugin => true,
586        }
587    }
588
589    /// Whether the parameter is required.
590    pub fn required(&self) -> bool {
591        match self {
592            Self::Native(info) => info.required,
593            Self::Closure(info) => matches!(&info.v, ClosureParamInfo::Pos { .. }),
594            Self::Plugin => false,
595        }
596    }
597
598    /// Whether the parameter is settable.
599    pub fn settable(&self) -> bool {
600        match self {
601            Self::Native(info) => info.settable,
602            Self::Closure(_) => false,
603            Self::Plugin => false,
604        }
605    }
606}
607
608/// A Typst function that is defined by a native Rust type that shadows a
609/// native Rust function.
610pub trait NativeFunc {
611    /// Get the function for the native Rust type.
612    fn func() -> Func {
613        Func::from(Self::data())
614    }
615
616    /// Get the function data for the native Rust function.
617    fn data() -> &'static NativeFuncData;
618}
619
620/// Defines a native function.
621#[derive(Debug)]
622pub struct NativeFuncData {
623    /// The implementation of the function.
624    pub function: NativeFuncPtr,
625    /// The function's normal name (e.g. `align`), as exposed to Typst.
626    pub name: &'static str,
627    /// The function's title case name (e.g. `Align`).
628    pub title: &'static str,
629    /// The documentation for this function as a string.
630    pub docs: &'static str,
631    /// Where the function is defined in the source code.
632    pub def_site: Option<DefSite>,
633    /// A list of alternate search terms for this function.
634    pub keywords: &'static [&'static str],
635    /// Whether this function makes use of context.
636    pub contextual: bool,
637    /// Definitions in the scope of the function.
638    pub scope: DynLazyLock<Scope>,
639    /// A list of parameter information for each parameter.
640    pub params: DynLazyLock<Vec<NativeParamInfo>>,
641    /// Information about the return value of this function.
642    pub returns: DynLazyLock<CastInfo>,
643}
644
645cast! {
646    &'static NativeFuncData,
647    self => Func::from(self).into_value(),
648}
649
650/// A pointer to a native function's implementation.
651pub struct NativeFuncPtr(pub &'static NativeFuncSignature);
652
653/// The signature of a native function's implementation.
654type NativeFuncSignature =
655    dyn Fn(&mut Engine, Tracked<Context>, &mut Args) -> SourceResult<Value> + Send + Sync;
656
657impl Debug for NativeFuncPtr {
658    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
659        f.pad("NativeFuncPtr(..)")
660    }
661}
662
663/// A `LazyLock` that uses a static closure for initialization instead of only
664/// working with function pointers.
665///
666/// Can be created from a normal function or closure by prepending with a `&`,
667/// e.g. `LazyLock::new(&|| "hello")`. Can be created from a dynamic closure
668/// by allocating and then leaking it. This is equivalent to having it
669/// statically allocated, but allows for it to be generated at runtime.
670type DynLazyLock<T> = LazyLock<T, &'static (dyn Fn() -> T + Send + Sync)>;
671
672/// Describes a function parameter.
673#[derive(Debug, Clone)]
674pub struct NativeParamInfo {
675    /// The parameter's name.
676    pub name: &'static str,
677    /// Documentation for the parameter.
678    pub docs: &'static str,
679    /// Where the parameter is defined in the source code.
680    pub def_site: Option<DefSite>,
681    /// Describe what values this parameter accepts.
682    pub input: CastInfo,
683    /// Creates an instance of the parameter's default value.
684    pub default: Option<fn() -> Value>,
685    /// Is the parameter positional?
686    pub positional: bool,
687    /// Is the parameter named?
688    ///
689    /// Can be true even if `positional` is true if the parameter can be given
690    /// in both variants.
691    pub named: bool,
692    /// Can the parameter be given any number of times?
693    pub variadic: bool,
694    /// Is the parameter required?
695    pub required: bool,
696    /// Is the parameter settable with a set rule?
697    pub settable: bool,
698}
699
700/// Distinguishes between variants of closures.
701#[derive(Debug, Hash)]
702pub enum ClosureNode {
703    /// A regular closure. Must always be castable to a `ast::Closure`.
704    Closure(SyntaxNode),
705    /// Synthetic closure used for `context` expressions. Can be any `ast::Expr`
706    /// and has no parameters.
707    Context(SyntaxNode),
708}
709
710/// A user-defined closure.
711#[derive(Debug, Hash)]
712pub struct Closure {
713    /// The closure's syntax node.
714    pub node: ClosureNode,
715    /// Default values of named parameters.
716    pub defaults: Vec<Value>,
717    /// Captured values from outer scopes.
718    pub captured: Scope,
719    /// The number of positional parameters in the closure.
720    pub num_pos_params: usize,
721}
722
723impl Closure {
724    /// The name of the closure.
725    pub fn name(&self) -> Option<&str> {
726        match self.node {
727            ClosureNode::Closure(ref node) => {
728                node.cast::<ast::Closure>()?.name().map(|ident| ident.as_str())
729            }
730            _ => None,
731        }
732    }
733
734    /// Returns details about this closure's parameters.
735    pub fn params(&self) -> impl Iterator<Item = Spanned<ClosureParamInfo>> {
736        let params = match self.node {
737            ClosureNode::Closure(ref node) => Some(
738                node.cast::<ast::Closure>()
739                    .expect("node to be an `ast::Closure`")
740                    .params()
741                    .children(),
742            ),
743            ClosureNode::Context(_) => None,
744        };
745
746        let mut defaults = self.defaults.iter();
747        params.into_iter().flatten().map(move |param| {
748            let info = match param {
749                ast::Param::Pos(pattern) => ClosureParamInfo::Pos {
750                    name: match pattern {
751                        ast::Pattern::Normal(ast::Expr::Ident(ident)) => {
752                            Some(ident.get().clone())
753                        }
754                        _ => None,
755                    },
756                },
757                ast::Param::Spread(spread) => ClosureParamInfo::Sink {
758                    name: spread.sink_ident().map(|ident| ident.get().clone()),
759                },
760                ast::Param::Named(named) => ClosureParamInfo::Named {
761                    name: named.name().get().clone(),
762                    default: defaults.next().unwrap().clone(),
763                },
764            };
765            Spanned::new(info, param.span())
766        })
767    }
768}
769
770cast! {
771    Closure,
772    self => Value::Func(self.into()),
773}
774
775/// Details about a closure parameter.
776#[derive(Debug, Clone)]
777pub enum ClosureParamInfo {
778    /// A positional parameter. It might have a name, but it could also be a
779    /// pattern.
780    Pos { name: Option<EcoString> },
781    /// A sink parameter. Might have a name, but could also just be a discarding
782    /// sink.
783    Sink { name: Option<EcoString> },
784    /// A named parameter with its name and default value.
785    Named { name: EcoString, default: Value },
786}