silex_css 0.1.0-beta.9

Next Generation High-Performance Rust Web Framework based on fine-grained reactivity and no-virtual-DOM architecture.
Documentation
use std::fmt::Display;

mod calc;
mod complex;
mod gradients;
mod shorthands;
mod units;

pub use calc::*;
pub use complex::*;
pub use gradients::*;
pub use shorthands::*;
pub use units::*;

/// 核心验证 Trait
/// 用于保证传入的值属于当前 CSS 属性合法的类型。
#[diagnostic::on_unimplemented(
    message = "类型 `{Self}` 无法作为有效的 CSS `{Prop}` 属性值使用",
    label = "无效的 CSS 属性类型",
    note = "请检查是否传入了错误的类型(例如将 Px 传给了 Color)。如果必须传入复杂的动态表达式,可以使用 `UnsafeCss::new(...)` 显式绕过。"
)]
pub trait ValidFor<Prop> {}

pub trait CssValue: Display {}
impl<T: Display> CssValue for T {}

#[derive(Clone, Debug, PartialEq, Default)]
pub struct UnsafeCss(pub Option<String>);
impl UnsafeCss {
    pub fn new<T: Display>(val: T) -> Self {
        Self(Some(val.to_string()))
    }
}
impl Display for UnsafeCss {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        if let Some(v) = &self.0 {
            write!(f, "{}", v)
        } else {
            Ok(())
        }
    }
}

#[derive(Clone, Debug, PartialEq)]
pub enum CssVarValue {
    Static(&'static str),
    Dynamic(String),
}

impl PartialEq<&str> for CssVarValue {
    fn eq(&self, other: &&str) -> bool {
        match self {
            Self::Static(s) => s == other,
            Self::Dynamic(s) => s == other,
        }
    }
}

impl Display for CssVarValue {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Static(s) => write!(f, "{}", s),
            Self::Dynamic(s) => write!(f, "{}", s),
        }
    }
}

/// CSS 变量类型,可通过 `css_var()` 函数创建。
/// 泛型 T 用于强类型校验,例如 `CssVar<Hex>` 仅在接收颜色的属性中有效。
#[derive(Clone, Debug, PartialEq)]
pub struct CssVar<T = ()>(pub CssVarValue, pub std::marker::PhantomData<T>);

impl<T: CssColor> CssVar<T> {
    pub fn alpha(self, alpha: f64) -> Self {
        Self(
            CssVarValue::Dynamic(format!(
                "color-mix(in srgb, {}, transparent {}%)",
                self.0,
                (1.0 - alpha) * 100.0
            )),
            std::marker::PhantomData,
        )
    }
}

impl<T> From<T> for CssVar<T>
where
    T: Display,
{
    fn from(val: T) -> Self {
        Self(
            CssVarValue::Dynamic(val.to_string()),
            std::marker::PhantomData,
        )
    }
}

impl<T> Default for CssVar<T> {
    fn default() -> Self {
        Self(CssVarValue::Static(""), std::marker::PhantomData)
    }
}

impl<T> Display for CssVar<T> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
}

/// 创建一个 CSS 变量引用。
/// 如果输入不带 `var()` 前缀,会自动包裹。
/// 返回的 CssVar<()> 对所有 CSS 属性有效(不安全模式)。
pub fn css_var(name: impl Display) -> CssVar<()> {
    let name_str = name.to_string();
    let val = if name_str.starts_with("var(") {
        name_str
    } else {
        format!("var({})", name_str)
    };
    CssVar(CssVarValue::Dynamic(val), std::marker::PhantomData)
}

// ==========================================
// 关键字 Enum 自动化
// ==========================================

macro_rules! define_css_enum {
    (ColorKeyword ($($prop:path),*) $rest:tt) => {
        define_css_enum!(@base ColorKeyword $rest);
        // We handle ColorKeyword's ValidFor impls manually in define_props! or via traits
    };
    ($name:ident ($($prop:path),*) { $($variant:ident => $val:expr),* $(,)? }) => {
        define_css_enum!(@base $name { $($variant => $val),* });
        $(impl crate::types::ValidFor<$prop> for $name {})*
    };
    (@base $name:ident { $($variant:ident => $val:expr),* $(,)? }) => {
        #[derive(Clone, Copy, Debug, PartialEq)]
        pub enum $name { $($variant),* }
        impl Display for $name {
            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                match self { $(Self::$variant => write!(f, $val)),* }
            }
        }
    };
}

include!("keywords_gen.rs");

// ==========================================
// 属性定义与基础约束自动化
// ==========================================

macro_rules! impl_valid_for_dimension {
    ($prop:ty) => {
        impl ValidFor<$prop> for Px {}
        impl ValidFor<$prop> for Percent {}
        impl ValidFor<$prop> for Rem {}
        impl ValidFor<$prop> for Em {}
        impl ValidFor<$prop> for Vw {}
        impl ValidFor<$prop> for Vh {}
        impl ValidFor<$prop> for CalcValue<LengthMark> {}
    };
}

macro_rules! impl_css_ops {
    ($t:ty, $trait:ident, $mark:ident) => {
        impl<R: $trait> std::ops::Add<R> for $t {
            type Output = CalcValue<$mark>;
            fn add(self, rhs: R) -> Self::Output {
                CalcValue::binary(self, " + ", rhs)
            }
        }
        impl<R: $trait> std::ops::Sub<R> for $t {
            type Output = CalcValue<$mark>;
            fn sub(self, rhs: R) -> Self::Output {
                CalcValue::binary(self, " - ", rhs)
            }
        }
        impl std::ops::Mul<f64> for $t {
            type Output = CalcValue<$mark>;
            fn mul(self, rhs: f64) -> Self::Output {
                CalcValue::binary(self, " * ", rhs)
            }
        }
        impl std::ops::Div<f64> for $t {
            type Output = CalcValue<$mark>;
            fn div(self, rhs: f64) -> Self::Output {
                CalcValue::binary(self, " / ", rhs)
            }
        }
    };
}

impl CssLength for Px {}
impl CssLength for Percent {}
impl CssLength for Rem {}
impl CssLength for Em {}
impl CssLength for Vw {}
impl CssLength for Vh {}

impl CssAngle for Deg {}
impl CssAngle for Rad {}
impl CssAngle for Turn {}

impl CssColor for Rgba {}
impl CssColor for Hex {}
impl CssColor for Hsl {}
impl CssColor for ColorKeyword {}

impl_css_ops!(Px, CssLength, LengthMark);
impl_css_ops!(Percent, CssLength, LengthMark);
impl_css_ops!(Rem, CssLength, LengthMark);
impl_css_ops!(Em, CssLength, LengthMark);
impl_css_ops!(Vw, CssLength, LengthMark);
impl_css_ops!(Vh, CssLength, LengthMark);
impl_css_ops!(CalcValue<LengthMark>, CssLength, LengthMark);

impl_css_ops!(Deg, CssAngle, AngleMark);
impl_css_ops!(Rad, CssAngle, AngleMark);
impl_css_ops!(Turn, CssAngle, AngleMark);
impl_css_ops!(CalcValue<AngleMark>, CssAngle, AngleMark);

macro_rules! define_props {
    ($( ($snake:ident, $kebab:expr, $pascal:ident, $group:ident) ),*) => {
        pub mod props {
            $( pub struct $pascal; )*
            pub struct Any;
        }

        // 所有属性默认支持 UnsafeCss 和无类型限制的 CssVar<()>
        $(
            impl ValidFor<props::$pascal> for UnsafeCss {}
            impl ValidFor<props::$pascal> for CssVar<()> {}
            // 核心:强类型 CssVar<T> 继承 T 的校验规则
            impl<T> ValidFor<props::$pascal> for CssVar<T> where T: ValidFor<props::$pascal> {}
        )*

        $(
            define_props!(@group $pascal, $group);
        )*
    };
    // 维度分组 (px, rem, vh 等)
    (@group $pascal:ident, Dimension) => {
        impl_valid_for_dimension!(props::$pascal);
    };
    // 颜色分组 (rgba, hex, hsl)
    (@group $pascal:ident, Color) => {
        impl ValidFor<props::$pascal> for Rgba {}
        impl ValidFor<props::$pascal> for Hex {}
        impl ValidFor<props::$pascal> for Hsl {}
        impl ValidFor<props::$pascal> for ColorKeyword {}
    };
    // 数字分组 (z-index, opacity 等)
    (@group $pascal:ident, Number) => {
        impl ValidFor<props::$pascal> for i32 {}
        impl ValidFor<props::$pascal> for u32 {}
        impl ValidFor<props::$pascal> for i64 {}
        impl ValidFor<props::$pascal> for u64 {}
        impl ValidFor<props::$pascal> for isize {}
        impl ValidFor<props::$pascal> for usize {}
        impl ValidFor<props::$pascal> for f64 {}
        impl ValidFor<props::$pascal> for f32 {}
    };
    // 复杂/自定义分组 (background, border, transform)
    (@group $pascal:ident, Custom) => {
        impl ValidFor<props::$pascal> for String {}
        impl ValidFor<props::$pascal> for &'static str {}
        impl_valid_for_dimension!(props::$pascal);
        impl ValidFor<props::$pascal> for Rgba {}
        impl ValidFor<props::$pascal> for Hex {}
        impl ValidFor<props::$pascal> for Hsl {}
        impl ValidFor<props::$pascal> for ColorKeyword {}
        impl ValidFor<props::$pascal> for NoneValue {}
    };
    // 复合属性专用 (如 border, margin)
    (@group $pascal:ident, Shorthand) => {
        impl ValidFor<props::$pascal> for String {}
        impl ValidFor<props::$pascal> for &'static str {}
        impl_valid_for_dimension!(props::$pascal);
        impl ValidFor<props::$pascal> for Rgba {}
        impl ValidFor<props::$pascal> for Hex {}
        impl ValidFor<props::$pascal> for Hsl {}
        impl ValidFor<props::$pascal> for ColorKeyword {}
        impl ValidFor<props::$pascal> for NoneValue {}
        impl ValidFor<props::$pascal> for i32 {}
        impl ValidFor<props::$pascal> for f64 {}
    };
    // 关键字分组 (由 define_css_enum 补充)
    (@group $pascal:ident, Keyword) => {};
    // 复杂属性分组 (transform, grid-template-areas 等)
    (@group $pascal:ident, Complex) => {
        impl ValidFor<props::$pascal> for String {}
        impl ValidFor<props::$pascal> for &'static str {}
    };
    // 透明度分组
    (@group $pascal:ident, Alpha) => {
        impl ValidFor<props::$pascal> for f64 {}
        impl ValidFor<props::$pascal> for f32 {}
        impl ValidFor<props::$pascal> for Percent {}
        impl ValidFor<props::$pascal> for String {}
        impl ValidFor<props::$pascal> for &'static str {}
    };
}

// 调用中心注册表执行代码生成
crate::for_all_properties!(define_props);

// --- 手动补充跨组约束 ---
impl ValidFor<props::Border> for BorderValue {}
impl ValidFor<props::BorderTop> for BorderValue {}
impl ValidFor<props::BorderRight> for BorderValue {}
impl ValidFor<props::BorderBottom> for BorderValue {}
impl ValidFor<props::BorderLeft> for BorderValue {}
impl ValidFor<props::BorderInlineStart> for BorderValue {}
impl ValidFor<props::BorderInlineEnd> for BorderValue {}
impl ValidFor<props::BorderBlockStart> for BorderValue {}
impl ValidFor<props::BorderBlockEnd> for BorderValue {}
impl ValidFor<props::Background> for Url {}
impl ValidFor<props::BackgroundImage> for Url {}
impl<T: Display> ValidFor<props::Any> for T {}

// 响应式集成后的注册
macro_rules! impl_into_rx_for_css {
    ($($t:ty),*) => {
        $(
            impl silex_core::traits::RxValue for $t {
                type Value = $t;
            }

            impl silex_core::traits::IntoRx for $t {
                type RxType = silex_core::Rx<$t, silex_core::RxValueKind>;
                fn into_rx(self) -> Self::RxType {
                    silex_core::Rx::new_constant(self)
                }
                fn is_constant(&self) -> bool { true }
            }

            impl silex_core::traits::IntoSignal for $t {
                fn into_signal(self) -> silex_core::reactivity::Signal<$t>
                where
                    Self: Sized + 'static,
                    $t: Sized + Clone + 'static,
                {
                    silex_core::reactivity::Signal::from(self)
                }
            }
        )*
    };
}

impl<T: 'static> silex_core::traits::RxValue for CssVar<T> {
    type Value = Self;
}
impl<T: 'static> silex_core::traits::IntoRx for CssVar<T> {
    type RxType = silex_core::Rx<Self, silex_core::RxValueKind>;
    fn into_rx(self) -> Self::RxType {
        silex_core::Rx::new_constant(self)
    }
    fn is_constant(&self) -> bool {
        true
    }
}
impl<T: Clone + 'static> silex_core::traits::IntoSignal for CssVar<T> {
    fn into_signal(self) -> silex_core::reactivity::Signal<Self> {
        silex_core::reactivity::Signal::from(self)
    }
}

impl_into_rx_for_css!(
    Px,
    Percent,
    Rgba,
    Auto,
    Rem,
    Em,
    Vw,
    Vh,
    Hex,
    Hsl,
    NoneValue,
    Url,
    BorderValue,
    MarginValue,
    PaddingValue,
    FlexValue,
    TransitionValue,
    BackgroundValue,
    UnsafeCss,
    TransformValue,
    TransformBuilder,
    GridTemplateAreasValue,
    FontVariationSettingsValue,
    CalcValue<LengthMark>,
    CalcValue<AngleMark>,
    Deg,
    Rad,
    Turn,
    GradientValue
);

register_generated_keywords!(impl_into_rx_for_css);