use alloc::borrow::Cow;
use core::fmt;
use super::*;
use crate::property::Property;
#[derive(Clone, Debug, PartialEq)]
pub enum PropertyMeta {
Normal {
property: Property,
},
Important {
property: Property,
},
DebugGroup {
original_name_value: Box<(String, String)>,
properties: Box<[Property]>,
important: bool,
disabled: bool,
},
}
impl PropertyMeta {
pub fn new_debug_properties(source: &str) -> Vec<Self> {
parser::parse_inline_style(source, parser::StyleParsingDebugMode::Debug).0
}
pub fn to_debug_state(&self, disabled: bool) -> Self {
match self {
Self::Normal { property } => Self::DebugGroup {
original_name_value: Box::new((
self.get_property_name().into(),
self.get_property_value_string(),
)),
properties: Box::new([property.clone()]),
important: false,
disabled,
},
Self::Important { property } => Self::DebugGroup {
original_name_value: Box::new((
self.get_property_name().into(),
self.get_property_value_string(),
)),
properties: Box::new([property.clone()]),
important: false,
disabled,
},
Self::DebugGroup {
original_name_value,
properties,
important,
..
} => Self::DebugGroup {
original_name_value: original_name_value.clone(),
properties: properties.clone(),
important: *important,
disabled,
},
}
}
pub fn is_important(&self) -> bool {
match self {
Self::Normal { .. } => false,
Self::Important { .. } => true,
Self::DebugGroup { important, .. } => *important,
}
}
pub fn get_property_name(&self) -> Cow<'static, str> {
match self {
Self::Normal { property } => property.get_property_name().into(),
Self::Important { property } => property.get_property_name().into(),
Self::DebugGroup {
original_name_value,
..
} => original_name_value.0.clone().into(),
}
}
pub fn get_property_value_string(&self) -> String {
match self {
Self::Normal { property } => property.get_property_value_string(),
Self::Important { property } => {
let mut v = property.get_property_value_string();
v.push_str(" !important");
v
}
Self::DebugGroup {
original_name_value,
..
} => original_name_value.1.clone(),
}
}
pub fn is_disabled(&self) -> bool {
match self {
Self::Normal { .. } => false,
Self::Important { .. } => false,
Self::DebugGroup { disabled, .. } => *disabled,
}
}
pub fn is_invalid(&self) -> bool {
match self {
Self::Normal { .. } => false,
Self::Important { .. } => false,
Self::DebugGroup { properties, .. } => properties.is_empty(),
}
}
pub fn is_deprecated(&self) -> bool {
match self {
Self::Normal { property, .. } | Self::Important { property, .. } => {
property.is_deprecated()
}
Self::DebugGroup { .. } => false,
}
}
pub fn merge_to_node_properties(
&self,
node_properties: &mut NodeProperties,
parent_node_properties: Option<&NodeProperties>,
current_font_size: f32,
) {
match self {
PropertyMeta::Normal { property: p } => {
node_properties.merge_property(p, parent_node_properties, current_font_size)
}
PropertyMeta::Important { property: p } => {
node_properties.merge_property(p, parent_node_properties, current_font_size)
}
PropertyMeta::DebugGroup {
properties,
disabled,
..
} => {
if !disabled {
for p in &**properties {
node_properties.merge_property(p, parent_node_properties, current_font_size)
}
}
}
}
}
pub fn iter(&self) -> PropertyMetaIter<'_> {
PropertyMetaIter { pm: self, cur: 0 }
}
#[cfg(test)]
#[doc(hidden)]
pub fn property(&self) -> Option<Property> {
match self {
Self::Normal { property } | Self::Important { property } => Some(property.clone()),
Self::DebugGroup { .. } => None,
}
}
}
impl fmt::Display for PropertyMeta {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
if self.is_disabled() {
write!(
f,
"/* {}: {}; */",
self.get_property_name(),
self.get_property_value_string(),
)
} else {
write!(
f,
"{}: {};",
self.get_property_name(),
self.get_property_value_string(),
)
}
}
}
pub struct PropertyMetaIter<'a> {
pm: &'a PropertyMeta,
cur: usize,
}
impl<'a> Iterator for PropertyMetaIter<'a> {
type Item = &'a Property;
fn next(&mut self) -> Option<Self::Item> {
match self.pm {
PropertyMeta::Normal { property } | PropertyMeta::Important { property } => {
if self.cur == 0 {
self.cur = 1;
Some(property)
} else {
None
}
}
PropertyMeta::DebugGroup { properties, .. } => {
if self.cur < properties.len() {
let ret = &properties[self.cur];
self.cur += 1;
Some(ret)
} else {
None
}
}
}
}
}
#[derive(Clone, Debug)]
pub struct Rule {
pub(crate) selector: Selector,
pub(crate) properties: Vec<PropertyMeta>,
pub(crate) media: Option<Rc<Media>>,
pub(super) index: u32,
pub(crate) has_font_size: bool,
}
impl fmt::Display for Rule {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let media_queries = self.get_media_query_string_list();
for media in media_queries.iter() {
write!(f, "@media {media} {{ ")?;
}
let selector = self.get_selector_string();
write!(f, "{selector} {{ ")?;
for prop in self.properties() {
write!(
f,
"{}: {}; ",
prop.get_property_name(),
prop.get_property_value_string()
)?;
}
write!(f, "}}")?;
for _ in media_queries.iter() {
write!(f, " }}")?;
}
Ok(())
}
}
impl Rule {
pub fn new_empty() -> Box<Self> {
Box::new(Self {
selector: Selector::star_selector(),
properties: Vec::with_capacity(0),
media: None,
index: 0,
has_font_size: false,
})
}
pub(crate) fn new(
selector: Selector,
properties: Vec<PropertyMeta>,
media: Option<Rc<Media>>,
) -> Box<Self> {
Self::new_with_index(selector, properties, media, 0)
}
pub(crate) fn new_with_index(
selector: Selector,
properties: Vec<PropertyMeta>,
media: Option<Rc<Media>>,
index: u32,
) -> Box<Self> {
let mut has_font_size = false;
for p in properties.iter() {
match p {
PropertyMeta::Normal { property } | PropertyMeta::Important { property } => {
match property {
Property::FontSize(..) => {
has_font_size = true;
}
_ => {}
}
}
PropertyMeta::DebugGroup { properties, .. } => {
for property in properties.iter() {
match property {
Property::FontSize(..) => {
has_font_size = true;
}
_ => {}
}
}
}
}
}
Box::new(Self {
selector,
properties,
media,
index,
has_font_size,
})
}
pub fn from_parts_str<'a>(
media_query_str_list: impl IntoIterator<Item = &'a str>,
selector_str: &str,
) -> Result<Box<Self>, Warning> {
let mut media = None;
for (index, media_str) in media_query_str_list.into_iter().enumerate() {
let cur_media = parser::parse_media_expression_only(media_str).map_err(|mut w| {
w.message = format!("{} (in media index {})", w.message.as_str(), index).into();
w
})?;
media = Some(Rc::new(cur_media));
}
let selector = parser::parse_selector_only(selector_str)?;
Ok(Self::new(selector, vec![], media))
}
pub fn modify_selector(&self, selector_str: &str) -> Result<Box<Self>, Warning> {
let media = self.media.clone();
let selector = parser::parse_selector_only(selector_str)?;
let properties = self.properties.clone();
Ok(Self::new(selector, properties, media))
}
pub fn add_properties(&self, p: impl IntoIterator<Item = PropertyMeta>) -> Box<Self> {
let media = self.media.clone();
let selector = self.selector.clone();
let mut properties = self.properties.clone();
for p in p {
properties.push(p);
}
Self::new(selector, properties, media)
}
pub fn set_property_disabled(&self, index: usize, disabled: bool) -> Option<Box<Self>> {
let media = self.media.clone();
let selector = self.selector.clone();
let mut properties = self.properties.clone();
if index < properties.len() {
properties[index] = properties[index].to_debug_state(disabled);
Some(Self::new(selector, properties, media))
} else {
None
}
}
pub fn remove_property(&self, index: usize) -> Option<Box<Self>> {
let media = self.media.clone();
let selector = self.selector.clone();
let mut properties = self.properties.clone();
if index < properties.len() {
properties.remove(index);
Some(Self::new(selector, properties, media))
} else {
None
}
}
pub fn replace_properties(
&self,
range: impl core::ops::RangeBounds<usize>,
p: impl IntoIterator<Item = PropertyMeta>,
) -> Option<Box<Self>> {
use core::ops::Bound;
let media = self.media.clone();
let selector = self.selector.clone();
let mut properties = self.properties.clone();
let no_overflow = match range.end_bound() {
Bound::Unbounded => true,
Bound::Included(stp) => *stp < properties.len(),
Bound::Excluded(stp) => *stp <= properties.len(),
};
let no_reversed = match range.start_bound() {
Bound::Unbounded => true,
Bound::Included(st) => match range.end_bound() {
Bound::Unbounded => true,
Bound::Included(stp) => *st <= *stp,
Bound::Excluded(stp) => *st < *stp,
},
Bound::Excluded(st) => match range.end_bound() {
Bound::Unbounded => true,
Bound::Included(stp) => *st < *stp,
Bound::Excluded(stp) => st + 1 < *stp,
},
};
if no_overflow && no_reversed {
properties.splice(range, p);
Some(Self::new(selector, properties, media))
} else {
None
}
}
pub fn get_media_query_string_list(&self) -> Vec<String> {
let mut list = vec![];
if let Some(x) = &self.media {
x.to_media_query_string_list(&mut list);
}
list
}
pub fn get_selector_string(&self) -> String {
format!("{}", self.selector)
}
pub fn properties(&self) -> impl Iterator<Item = &PropertyMeta> {
self.properties.iter()
}
pub(crate) fn match_query<L: LengthNum, T: StyleNode>(
&self,
query: &[T],
media_query_status: &MediaQueryStatus<L>,
sheet_style_scope: Option<NonZeroUsize>,
) -> Option<u16> {
match &self.media {
Some(media) => {
if !media.is_valid(media_query_status) {
return None;
}
}
None => {}
}
self.selector.match_query(query, sheet_style_scope)
}
}