Repository: typst/typst
File: crates/typst-library/src/foundations/label.rs (Rust)

Code before:
/// get rejected.
///
/// Note that there is a syntactical difference when using the dedicated syntax
/// for this function. In the code below, the `[<a>]` terminates the heading and
/// thus attaches to the heading itself, whereas the `[#label("b")]` is part of
/// the heading and thus attaches to the heading's text.
///
/// ```typ
/// // Equivalent to `#heading[Introduction] <a>`.
/// = Introduction <a>
///
/// // Equivalent to `#heading[Conclusion #label("b")]`.
/// = Conclusion #label("b")
/// ```
///
/// Currently, labels can only be attached to elements in markup mode, not in
/// code mode. This might change in the future.
#[ty(scope, cast)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Label(PicoStr);

impl Label {
    /// Creates a label from an interned string.
    /// Callers need to ensure the given string is not empty.
    pub fn new(name: PicoStr) -> Self {
        debug_assert!(name != PicoStr::EMPTY);
        Self(name)
    }

    /// Resolves the label to a string.
    pub fn resolve(self) -> ResolvedPicoStr {
        self.0.resolve()
    }

    /// Turns this label into its inner interned string.
    pub fn into_inner(self) -> PicoStr {
        self.0
    }
}

#[scope]
impl Label {
    /// Creates a label from a string. Fails for empty strings.
    #[func(constructor)]
    pub fn construct(
        /// The name of the label.
        name: Str,
    ) -> StrResult<Label> {
        if name.is_empty() {
            bail!("expected non-empty label name");
        }

Review comment (suggestion):
Having the assertion here makes it more likely that we have an empty label or runtime panic because some callsite didn't properly check it. We should instead return `Option` here move the check to the callsites, potentially unwrapping in cases where we're sure that the string is non-empty.

Code after:
/// Note that there is a syntactical difference when using the dedicated syntax
/// for this function. In the code below, the `[<a>]` terminates the heading and
/// thus attaches to the heading itself, whereas the `[#label("b")]` is part of
/// the heading and thus attaches to the heading's text.
///
/// ```typ
/// // Equivalent to `#heading[Introduction] <a>`.
/// = Introduction <a>
///
/// // Equivalent to `#heading[Conclusion #label("b")]`.
/// = Conclusion #label("b")
/// ```
///
/// Currently, labels can only be attached to elements in markup mode, not in
/// code mode. This might change in the future.
#[ty(scope, cast)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Label(PicoStr);

impl Label {
    /// Creates a label from an interned string.
    ///
    /// Returns `None` if the given string is empty.
    pub fn new(name: PicoStr) -> Option<Self> {
        const EMPTY: PicoStr = PicoStr::constant("");
        (name != EMPTY).then_some(Self(name))
    }

    /// Resolves the label to a string.
    pub fn resolve(self) -> ResolvedPicoStr {
        self.0.resolve()
    }

    /// Turns this label into its inner interned string.
    pub fn into_inner(self) -> PicoStr {
        self.0
    }
}

#[scope]
impl Label {
    /// Creates a label from a string.
    #[func(constructor)]
    pub fn construct(
        /// The name of the label. Must not be empty.
        name: Str,
    ) -> StrResult<Label> {
        if name.is_empty() {
            bail!("label name must not be empty");
        }