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::*;
#[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),
}
}
}
#[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)
}
}
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)
}
macro_rules! define_css_enum {
(ColorKeyword ($($prop:path),*) $rest:tt) => {
define_css_enum!(@base ColorKeyword $rest);
};
($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;
}
$(
impl ValidFor<props::$pascal> for UnsafeCss {}
impl ValidFor<props::$pascal> for CssVar<()> {}
impl<T> ValidFor<props::$pascal> for CssVar<T> where T: ValidFor<props::$pascal> {}
)*
$(
define_props!(@group $pascal, $group);
)*
};
(@group $pascal:ident, Dimension) => {
impl_valid_for_dimension!(props::$pascal);
};
(@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 {}
};
(@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 {}
};
(@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 {}
};
(@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 {}
};
(@group $pascal:ident, Keyword) => {};
(@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);