Repository: typst/typst
File: crates/typst/src/foundations/args.rs (Rust)

Code before:
        #[external]
        #[variadic]
        arguments: Vec<Value>,
    ) -> Args {
        args.take()
    }

    /// Returns the positional argument at the specified index, or the named
    /// argument with the specified name.
    ///
    /// If the key is an [integer]($int), this is equivalent to first calling
    /// [`pos`]($arguments.pos) and then [`array.at`]. If it is a [string]($str),
    /// this is equivalent to first calling [`named`]($arguments.named) and then
    /// [`dictionary.at`].
    #[func]
    pub fn at(
        &self,
        /// The index or name of the argument to get.
        key: ArgumentKey,
        /// A default value to return if the key is invalid.
        #[named]
        default: Option<Value>,
    ) -> StrResult<Value> {
        match &key {
            ArgumentKey::Index(index) => self.to_pos().at(*index, default).ok(),
            ArgumentKey::Name(name) => self.to_named().at(name.clone(), default).ok(),
        }
        .ok_or_else(|| missing_key_no_default(key))
    }

    /// Returns the captured positional arguments as an array.
    #[func(name = "pos", title = "Positional")]
    pub fn to_pos(&self) -> Array {
        self.items
            .iter()
            .filter(|item| item.name.is_none())
            .map(|item| item.value.v.clone())
            .collect()
    }

    /// Returns the captured named arguments as a dictionary.
    #[func(name = "named")]
    pub fn to_named(&self) -> Dict {
        self.items
            .iter()
            .filter_map(|item| item.name.clone().map(|name| (name, item.value.v.clone())))
            .collect()
    }
}

impl Debug for Args {

Review comment (performance):
I think if we already have this method, we should make it more efficient than calling `to_pos` and `to_named` internally, since those are quite costly.

Code after:
            ArgumentKey::Name(name) => {
                self.items.iter().rfind(|item| item.name.as_ref() == Some(name))
            }
        };
        item.map(|item| &item.value.v)
    }
}

#[scope]
impl Args {
    /// Construct spreadable arguments in place.
    ///
    /// This function behaves like `{let args(..sink) = sink}`.
    ///
    /// ```example
    /// #let args = arguments(stroke: red, inset: 1em, [Body])
    /// #box(..args)
    /// ```
    #[func(constructor)]
    pub fn construct(
        /// The real arguments (the other argument is just for the docs).
        /// The docs argument cannot be called `args`.
        args: &mut Args,
        /// The arguments to construct.
        #[external]
        #[variadic]
        arguments: Vec<Value>,
    ) -> Args {
        args.take()
    }

    /// Returns the positional argument at the specified index, or the named
    /// argument with the specified name.
    ///
    /// If the key is an [integer]($int), this is equivalent to first calling
    /// [`pos`]($arguments.pos) and then [`array.at`]. If it is a [string]($str),
    /// this is equivalent to first calling [`named`]($arguments.named) and then
    /// [`dictionary.at`].
    #[func]
    pub fn at(
        &self,
        /// The index or name of the argument to get.
        key: ArgumentKey,
        /// A default value to return if the key is invalid.
        #[named]
        default: Option<Value>,
    ) -> StrResult<Value> {
        self.get(&key)
            .cloned()
            .or(default)
            .ok_or_else(|| missing_key_no_default(key))