bun_css 0.1.0

A Rust-native programmable browser runtime built on Servo and SpiderMonkey
#![warn(unused_must_use)]
use crate as css;
use crate::PrintErr;
use crate::Printer;
use crate::css_values::color::CssColor;
use crate::css_values::length::LengthValue as Length;

bitflags::bitflags! {
    #[derive(Clone, Copy, PartialEq, Eq, Default)]
    pub(crate) struct TextTransformOther: u8 {
        /// Puts all typographic character units in full-width form.
        const FULL_WIDTH     = 1 << 0;
        /// Converts all small Kana characters to the equivalent full-size Kana.
        const FULL_SIZE_KANA = 1 << 1;
    }
}

bitflags::bitflags! {
    /// A value for the [text-decoration-line](https://www.w3.org/TR/2020/WD-css-text-decor-4-20200506/#text-decoration-line-property) property.
    ///
    /// Multiple lines may be specified by combining the flags.
    #[derive(Clone, Copy, PartialEq, Eq, Default)]
    pub(crate) struct TextDecorationLine: u8 {
        /// Each line of text is underlined.
        const UNDERLINE      = 1 << 0;
        /// Each line of text has a line over it.
        const OVERLINE       = 1 << 1;
        /// Each line of text has a line through the middle.
        const LINE_THROUGH   = 1 << 2;
        /// The text blinks.
        const BLINK          = 1 << 3;
        /// The text is decorated as a spelling error.
        const SPELLING_ERROR = 1 << 4;
        /// The text is decorated as a grammar error.
        const GRAMMAR_ERROR  = 1 << 5;
    }
}

/// A value for the [text-shadow](https://www.w3.org/TR/2020/WD-css-text-decor-4-20200506/#text-shadow-property) property.
#[derive(Clone, PartialEq)]
pub struct TextShadow {
    /// The color of the text shadow.
    pub color: CssColor,
    /// The x offset of the text shadow.
    pub x_offset: Length,
    /// The y offset of the text shadow.
    pub y_offset: Length,
    /// The blur radius of the text shadow.
    pub blur: Length,
    /// The spread distance of the text shadow.
    pub spread: Length, // added in Level 4 spec
}

impl TextShadow {
    pub(crate) fn parse(input: &mut css::Parser) -> css::Result<Self> {
        let mut color: Option<CssColor> = None;
        type Lengths = (Length, Length, Length, Length);
        let mut lengths: Option<Lengths> = None;

        loop {
            if lengths.is_none() {
                let value = input.try_parse(|i: &mut css::Parser| -> css::Result<Lengths> {
                    let horizontal = Length::parse(i)?;
                    let vertical = Length::parse(i)?;
                    let blur = i.try_parse(Length::parse).ok().unwrap_or_else(Length::zero);
                    let spread = i.try_parse(Length::parse).ok().unwrap_or_else(Length::zero);
                    Ok((horizontal, vertical, blur, spread))
                });

                if let Ok(v) = value {
                    lengths = Some(v);
                    continue;
                }
            }

            if color.is_none() {
                if let Ok(value) = input.try_parse(CssColor::parse) {
                    color = Some(value);
                    continue;
                }
            }

            break;
        }

        let Some(l) = lengths else {
            return Err(input.new_error(css::BasicParseErrorKind::qualified_rule_invalid));
        };
        Ok(Self {
            color: color.unwrap_or(CssColor::CurrentColor),
            x_offset: l.0,
            y_offset: l.1,
            blur: l.2,
            spread: l.3,
        })
    }

    pub(crate) fn to_css(&self, dest: &mut Printer) -> Result<(), PrintErr> {
        self.x_offset.to_css(dest)?;
        dest.write_char(b' ')?;
        self.y_offset.to_css(dest)?;

        if self.blur != Length::zero() || self.spread != Length::zero() {
            dest.write_char(b' ')?;
            self.blur.to_css(dest)?;

            if self.spread != Length::zero() {
                dest.write_char(b' ')?;
                self.spread.to_css(dest)?;
            }
        }

        if self.color != CssColor::CurrentColor {
            dest.write_char(b' ')?;
            self.color.to_css(dest)?;
        }

        Ok(())
    }

    pub(crate) fn is_compatible(&self, browsers: &css::targets::Browsers) -> bool {
        self.color.is_compatible(browsers)
            && self.x_offset.is_compatible(browsers)
            && self.y_offset.is_compatible(browsers)
            && self.blur.is_compatible(browsers)
            && self.spread.is_compatible(browsers)
    }

    // Zig: `pub fn eql` via `css.implementEql(@This(), ...)` — field-wise equality.
    // Ported as `#[derive(PartialEq)]` above; callers use `==`.

    pub(crate) fn deep_clone(&self, alloc: &bun_alloc::Arena) -> Self {
        // TODO(port): Zig used reflection-based `css.implementDeepClone`. Fields here
        // are value types, so a plain Clone is equivalent; arena param retained for
        // signature compatibility with the CSS deep_clone protocol.
        let _ = alloc;
        self.clone()
    }
}

// Forward to the inherent method so the blanket `SmallList<T,N>` impl in
// `crate::generics` (IsCompatible) applies to `SmallList<TextShadow,1>`.
// (DeepClone is bridged via `bridge_deep_clone!(TextShadow)` in generics.rs.)
impl css::generics::IsCompatible for TextShadow {
    #[inline]
    fn is_compatible(&self, browsers: &css::targets::Browsers) -> bool {
        self.is_compatible(browsers)
    }
}

/// A value for the [direction](https://drafts.csswg.org/css-writing-modes-3/#direction) property.
// Zig wires eql/hash/parse/toCss/deepClone via `css.DefineEnumProperty(@This())`.
#[derive(
    Debug, Clone, Copy, PartialEq, Eq, Hash, crate::DefineEnumProperty, crate::generics::CssHash,
)]
pub enum Direction {
    /// This value sets inline base direction (bidi directionality) to line-left-to-line-right.
    Ltr,
    /// This value sets inline base direction (bidi directionality) to line-right-to-line-left.
    Rtl,
}

// ported from: src/css/properties/text.zig