Repository: typst/typst
File: crates/typst/src/layout/align.rs (Rust)

Code before:
impl OuterVAlignment {
    /// Resolve the axis alignment based on the vertical direction.
    pub const fn fix(self) -> FixedAlignment {
        match self {
            Self::Top => FixedAlignment::Start,
            Self::Bottom => FixedAlignment::End,
        }
    }
}

impl From<OuterVAlignment> for VAlignment {
    fn from(value: OuterVAlignment) -> Self {
        match value {
            OuterVAlignment::Top => Self::Top,
            OuterVAlignment::Bottom => Self::Bottom,
        }
    }
}

impl From<VAlignment> for OuterVAlignment {
    fn from(value: VAlignment) -> Self {
        match value {
            VAlignment::Top => OuterVAlignment::Top,
            VAlignment::Bottom => OuterVAlignment::Bottom,
            VAlignment::Horizon => unreachable!(),
        }
    }
}

cast! {
    OuterVAlignment,
    self => VAlignment::from(self).into_value(),
    align: Alignment => match align {
        Alignment::V(VAlignment::Top) => Self::Top,
        Alignment::V(VAlignment::Bottom) => Self::Bottom,
        v => bail!("expected `top` or `bottom`, found {}", v.repr()),
    }
}

/// An internal representation that combines horizontal or vertical alignments. The
/// allowed alignment positions are designated by the type parameter `H` and `V`.
///
/// This is not user-visible, but an internal type to impose type safety. For example,
/// `SpecificAlignment<HAlignment, OuterVAlignment>` does not allow vertical alignment
/// position "center", because `V = OuterVAlignment` doesn't have it.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum SpecificAlignment<H, V>
where
    H: Into<HAlignment> + Copy,
    V: Into<VAlignment> + Copy,
    HAlignment: Into<H>,

Review comment (suggestion):
The last path isn't unreachable; we should use a fallible trait instead (returning a Result), as user code can trigger this path (in theory).

In fact, we only ever rely on FromValue anyway, which is perhaps a bit odd. One possible approach here is to switch to `TryFrom<HAlignment/VAlignment>` everywhere, and then have the FromValue implementations just call the TryFrom impl and propagate the error up.

Code after:
    }
}

impl TryFrom<Alignment> for VAlignment {
    type Error = EcoString;

    fn try_from(value: Alignment) -> StrResult<Self> {
        match value {
            Alignment::V(v) => Ok(v),
            v => bail!("expected `top`, `horizon`, or `bottom`, found {}", v.repr()),
        }
    }
}

cast! {
    VAlignment,
    self => Alignment::V(self).into_value(),
    align: Alignment => Self::try_from(align)?,
}

/// A vertical alignment which only allows `top` and `bottom`, thus excluding
/// `horizon`.
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum OuterVAlignment {
    #[default]
    Top,
    Bottom,
}

impl OuterVAlignment {
    /// Resolve the axis alignment based on the vertical direction.
    pub const fn fix(self) -> FixedAlignment {
        match self {
            Self::Top => FixedAlignment::Start,
            Self::Bottom => FixedAlignment::End,
        }
    }
}

impl From<OuterVAlignment> for VAlignment {
    fn from(value: OuterVAlignment) -> Self {
        match value {
            OuterVAlignment::Top => Self::Top,
            OuterVAlignment::Bottom => Self::Bottom,
        }
    }
}

impl TryFrom<Alignment> for OuterVAlignment {
    type Error = EcoString;