use alloc::{string::String, vec::Vec};
use core::fmt;
use crate::{
css_properties::{CssProperty, CssPropertyType},
AzString,
};
#[derive(Debug, Default, PartialEq, PartialOrd, Clone)]
#[repr(C)]
pub struct Css {
pub stylesheets: StylesheetVec,
}
impl_vec!(Stylesheet, StylesheetVec, StylesheetVecDestructor);
impl_vec_mut!(Stylesheet, StylesheetVec);
impl_vec_debug!(Stylesheet, StylesheetVec);
impl_vec_partialord!(Stylesheet, StylesheetVec);
impl_vec_clone!(Stylesheet, StylesheetVec, StylesheetVecDestructor);
impl_vec_partialeq!(Stylesheet, StylesheetVec);
impl Css {
pub fn is_empty(&self) -> bool {
self.stylesheets.iter().all(|s| s.rules.as_ref().is_empty())
}
pub fn new(stylesheets: Vec<Stylesheet>) -> Self {
Self {
stylesheets: stylesheets.into(),
}
}
#[cfg(feature = "parser")]
pub fn from_string(s: crate::AzString) -> Self {
crate::parser::new_from_str(s.as_str()).unwrap_or_default()
}
}
#[derive(Debug, Default, PartialEq, PartialOrd, Clone)]
#[repr(C)]
pub struct Stylesheet {
pub rules: CssRuleBlockVec,
}
impl_vec!(CssRuleBlock, CssRuleBlockVec, CssRuleBlockVecDestructor);
impl_vec_mut!(CssRuleBlock, CssRuleBlockVec);
impl_vec_debug!(CssRuleBlock, CssRuleBlockVec);
impl_vec_partialord!(CssRuleBlock, CssRuleBlockVec);
impl_vec_clone!(CssRuleBlock, CssRuleBlockVec, CssRuleBlockVecDestructor);
impl_vec_partialeq!(CssRuleBlock, CssRuleBlockVec);
impl Stylesheet {
pub fn new(rules: Vec<CssRuleBlock>) -> Self {
Self {
rules: rules.into(),
}
}
}
impl From<Vec<CssRuleBlock>> for Stylesheet {
fn from(rules: Vec<CssRuleBlock>) -> Self {
Self {
rules: rules.into(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[repr(C, u8)]
pub enum CssDeclaration {
Static(CssProperty),
Dynamic(DynamicCssProperty),
}
impl CssDeclaration {
pub const fn new_static(prop: CssProperty) -> Self {
CssDeclaration::Static(prop)
}
pub const fn new_dynamic(prop: DynamicCssProperty) -> Self {
CssDeclaration::Dynamic(prop)
}
pub fn get_type(&self) -> CssPropertyType {
use self::CssDeclaration::*;
match self {
Static(s) => s.get_type(),
Dynamic(d) => d.default_value.get_type(),
}
}
pub fn is_inheritable(&self) -> bool {
use self::CssDeclaration::*;
match self {
Static(s) => s.get_type().is_inheritable(),
Dynamic(d) => d.is_inheritable(),
}
}
pub fn can_trigger_relayout(&self) -> bool {
use self::CssDeclaration::*;
match self {
Static(s) => s.get_type().can_trigger_relayout(),
Dynamic(d) => d.can_trigger_relayout(),
}
}
pub fn to_str(&self) -> String {
use self::CssDeclaration::*;
match self {
Static(s) => format!("{:?}", s),
Dynamic(d) => format!("var(--{}, {:?})", d.dynamic_id, d.default_value),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[repr(C)]
pub struct DynamicCssProperty {
pub dynamic_id: AzString,
pub default_value: CssProperty,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[repr(C, u8)] pub enum CssPropertyValue<T> {
Auto,
None,
Initial,
Inherit,
Exact(T),
}
pub trait PrintAsCssValue {
fn print_as_css_value(&self) -> String;
}
impl<T: PrintAsCssValue> CssPropertyValue<T> {
pub fn get_css_value_fmt(&self) -> String {
match self {
CssPropertyValue::Auto => format!("auto"),
CssPropertyValue::None => format!("none"),
CssPropertyValue::Initial => format!("initial"),
CssPropertyValue::Inherit => format!("inherit"),
CssPropertyValue::Exact(e) => e.print_as_css_value(),
}
}
}
impl<T: fmt::Display> fmt::Display for CssPropertyValue<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::CssPropertyValue::*;
match self {
Auto => write!(f, "auto"),
None => write!(f, "none"),
Initial => write!(f, "initial"),
Inherit => write!(f, "inherit"),
Exact(e) => write!(f, "{}", e),
}
}
}
impl<T> From<T> for CssPropertyValue<T> {
fn from(c: T) -> Self {
CssPropertyValue::Exact(c)
}
}
impl<T> CssPropertyValue<T> {
#[inline]
pub fn map_property<F: Fn(T) -> U, U>(self, map_fn: F) -> CssPropertyValue<U> {
match self {
CssPropertyValue::Exact(c) => CssPropertyValue::Exact(map_fn(c)),
CssPropertyValue::Auto => CssPropertyValue::Auto,
CssPropertyValue::None => CssPropertyValue::None,
CssPropertyValue::Initial => CssPropertyValue::Initial,
CssPropertyValue::Inherit => CssPropertyValue::Inherit,
}
}
#[inline]
pub fn get_property(&self) -> Option<&T> {
match self {
CssPropertyValue::Exact(c) => Some(c),
_ => None,
}
}
#[inline]
pub fn get_property_owned(self) -> Option<T> {
match self {
CssPropertyValue::Exact(c) => Some(c),
_ => None,
}
}
#[inline]
pub fn is_auto(&self) -> bool {
match self {
CssPropertyValue::Auto => true,
_ => false,
}
}
#[inline]
pub fn is_none(&self) -> bool {
match self {
CssPropertyValue::None => true,
_ => false,
}
}
#[inline]
pub fn is_initial(&self) -> bool {
match self {
CssPropertyValue::Initial => true,
_ => false,
}
}
#[inline]
pub fn is_inherit(&self) -> bool {
match self {
CssPropertyValue::Inherit => true,
_ => false,
}
}
}
impl<T: Default> CssPropertyValue<T> {
#[inline]
pub fn get_property_or_default(self) -> Option<T> {
match self {
CssPropertyValue::Auto | CssPropertyValue::Initial => Some(T::default()),
CssPropertyValue::Exact(c) => Some(c),
CssPropertyValue::None | CssPropertyValue::Inherit => None,
}
}
}
impl<T: Default> Default for CssPropertyValue<T> {
#[inline]
fn default() -> Self {
CssPropertyValue::Exact(T::default())
}
}
impl DynamicCssProperty {
pub fn is_inheritable(&self) -> bool {
false
}
pub fn can_trigger_relayout(&self) -> bool {
self.default_value.get_type().can_trigger_relayout()
}
}
#[derive(Debug, Clone, PartialOrd, PartialEq)]
#[repr(C)]
pub struct CssRuleBlock {
pub path: CssPath,
pub declarations: CssDeclarationVec,
}
impl_vec!(
CssDeclaration,
CssDeclarationVec,
CssDeclarationVecDestructor
);
impl_vec_mut!(CssDeclaration, CssDeclarationVec);
impl_vec_debug!(CssDeclaration, CssDeclarationVec);
impl_vec_partialord!(CssDeclaration, CssDeclarationVec);
impl_vec_ord!(CssDeclaration, CssDeclarationVec);
impl_vec_clone!(
CssDeclaration,
CssDeclarationVec,
CssDeclarationVecDestructor
);
impl_vec_partialeq!(CssDeclaration, CssDeclarationVec);
impl_vec_eq!(CssDeclaration, CssDeclarationVec);
impl_vec_hash!(CssDeclaration, CssDeclarationVec);
impl CssRuleBlock {
pub fn new(path: CssPath, declarations: Vec<CssDeclaration>) -> Self {
Self {
path,
declarations: declarations.into(),
}
}
}
pub type CssContentGroup<'a> = Vec<&'a CssPathSelector>;
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub enum NodeTypeTag {
Body,
Div,
Br,
P,
Img,
IFrame,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum NodeTypeTagParseError<'a> {
Invalid(&'a str),
}
impl<'a> fmt::Display for NodeTypeTagParseError<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self {
NodeTypeTagParseError::Invalid(e) => write!(f, "Invalid node type: {}", e),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum NodeTypeTagParseErrorOwned {
Invalid(String),
}
impl<'a> NodeTypeTagParseError<'a> {
pub fn to_contained(&self) -> NodeTypeTagParseErrorOwned {
match self {
NodeTypeTagParseError::Invalid(s) => NodeTypeTagParseErrorOwned::Invalid(s.to_string()),
}
}
}
impl NodeTypeTagParseErrorOwned {
pub fn to_shared<'a>(&'a self) -> NodeTypeTagParseError<'a> {
match self {
NodeTypeTagParseErrorOwned::Invalid(s) => NodeTypeTagParseError::Invalid(s),
}
}
}
impl NodeTypeTag {
pub fn from_str(css_key: &str) -> Result<Self, NodeTypeTagParseError> {
match css_key {
"body" => Ok(NodeTypeTag::Body),
"div" => Ok(NodeTypeTag::Div),
"p" => Ok(NodeTypeTag::P),
"img" => Ok(NodeTypeTag::Img),
other => Err(NodeTypeTagParseError::Invalid(other)),
}
}
}
impl fmt::Display for NodeTypeTag {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
NodeTypeTag::Body => write!(f, "body"),
NodeTypeTag::Div => write!(f, "div"),
NodeTypeTag::Br => write!(f, "br"),
NodeTypeTag::P => write!(f, "p"),
NodeTypeTag::Img => write!(f, "img"),
NodeTypeTag::IFrame => write!(f, "iframe"),
}
}
}
#[derive(Clone, Hash, Default, PartialEq, Eq, PartialOrd, Ord)]
#[repr(C)]
pub struct CssPath {
pub selectors: CssPathSelectorVec,
}
impl_vec!(
CssPathSelector,
CssPathSelectorVec,
CssPathSelectorVecDestructor
);
impl_vec_debug!(CssPathSelector, CssPathSelectorVec);
impl_vec_partialord!(CssPathSelector, CssPathSelectorVec);
impl_vec_ord!(CssPathSelector, CssPathSelectorVec);
impl_vec_clone!(
CssPathSelector,
CssPathSelectorVec,
CssPathSelectorVecDestructor
);
impl_vec_partialeq!(CssPathSelector, CssPathSelectorVec);
impl_vec_eq!(CssPathSelector, CssPathSelectorVec);
impl_vec_hash!(CssPathSelector, CssPathSelectorVec);
impl CssPath {
pub fn new(selectors: Vec<CssPathSelector>) -> Self {
Self {
selectors: selectors.into(),
}
}
}
impl fmt::Display for CssPath {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for selector in self.selectors.as_ref() {
write!(f, "{}", selector)?;
}
Ok(())
}
}
impl fmt::Debug for CssPath {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[repr(C, u8)]
pub enum CssPathSelector {
Global,
Type(NodeTypeTag),
Class(AzString),
Id(AzString),
PseudoSelector(CssPathPseudoSelector),
DirectChildren,
Children,
}
impl Default for CssPathSelector {
fn default() -> Self {
CssPathSelector::Global
}
}
impl fmt::Display for CssPathSelector {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::CssPathSelector::*;
match &self {
Global => write!(f, "*"),
Type(n) => write!(f, "{}", n),
Class(c) => write!(f, ".{}", c),
Id(i) => write!(f, "#{}", i),
PseudoSelector(p) => write!(f, ":{}", p),
DirectChildren => write!(f, ">"),
Children => write!(f, " "),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[repr(C, u8)]
pub enum CssPathPseudoSelector {
First,
Last,
NthChild(CssNthChildSelector),
Hover,
Active,
Focus,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[repr(C, u8)]
pub enum CssNthChildSelector {
Number(u32),
Even,
Odd,
Pattern(CssNthChildPattern),
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[repr(C)]
pub struct CssNthChildPattern {
pub repeat: u32,
pub offset: u32,
}
impl fmt::Display for CssNthChildSelector {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::CssNthChildSelector::*;
match &self {
Number(u) => write!(f, "{}", u),
Even => write!(f, "even"),
Odd => write!(f, "odd"),
Pattern(p) => write!(f, "{}n + {}", p.repeat, p.offset),
}
}
}
impl fmt::Display for CssPathPseudoSelector {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::CssPathPseudoSelector::*;
match &self {
First => write!(f, "first"),
Last => write!(f, "last"),
NthChild(u) => write!(f, "nth-child({})", u),
Hover => write!(f, "hover"),
Active => write!(f, "active"),
Focus => write!(f, "focus"),
}
}
}
impl Css {
pub fn empty() -> Self {
Default::default()
}
pub fn sort_by_specificity(&mut self) {
self.stylesheets
.as_mut()
.iter_mut()
.for_each(|s| s.sort_by_specificity());
}
pub fn rules<'a>(&'a self) -> RuleIterator<'a> {
RuleIterator {
current_stylesheet: 0,
current_rule: 0,
css: self,
}
}
}
pub struct RuleIterator<'a> {
current_stylesheet: usize,
current_rule: usize,
css: &'a Css,
}
impl<'a> Iterator for RuleIterator<'a> {
type Item = &'a CssRuleBlock;
fn next(&mut self) -> Option<&'a CssRuleBlock> {
let current_stylesheet = self.css.stylesheets.get(self.current_stylesheet)?;
match current_stylesheet.rules.get(self.current_rule) {
Some(s) => {
self.current_rule += 1;
Some(s)
}
None => {
self.current_rule = 0;
self.current_stylesheet += 1;
self.next()
}
}
}
}
impl Stylesheet {
pub fn empty() -> Self {
Default::default()
}
pub fn sort_by_specificity(&mut self) {
self.rules
.as_mut()
.sort_by(|a, b| get_specificity(&a.path).cmp(&get_specificity(&b.path)));
}
}
fn get_specificity(path: &CssPath) -> (usize, usize, usize, usize) {
let id_count = path
.selectors
.iter()
.filter(|x| {
if let CssPathSelector::Id(_) = x {
true
} else {
false
}
})
.count();
let class_count = path
.selectors
.iter()
.filter(|x| {
if let CssPathSelector::Class(_) = x {
true
} else {
false
}
})
.count();
let div_count = path
.selectors
.iter()
.filter(|x| {
if let CssPathSelector::Type(_) = x {
true
} else {
false
}
})
.count();
(id_count, class_count, div_count, path.selectors.len())
}