css-minify 0.5.2

CSS parser and minificator based on nom
Documentation
use crate::optimizations::transformer::Transform;
use crate::structure::{Block, CssEntities, CssEntity, Media, Parameters, Supports};
use indexmap::map::IndexMap;

#[derive(Default, Debug, Clone)]
pub struct MergeBlocks;

impl Transform for MergeBlocks {
    fn transform_parameters(&self, parameters: Parameters) -> Parameters {
        parameters
    }

    fn transform(&self, entity: CssEntity) -> CssEntity {
        entity
    }

    fn transform_many(&self, entities: CssEntities) -> CssEntities {
        let mut blocks_to_merge = IndexMap::new();
        entities
            .0
            .iter()
            .filter_map(|e| match e {
                CssEntity::Block(b) => Some(b),
                _ => None,
            })
            .for_each(|b| {
                blocks_to_merge
                    .entry(b.selectors.clone().to_string())
                    .and_modify(|block: &mut Block| {
                        b.parameters.0.iter().for_each(|(n, v)| {
                            block.parameters.0.insert(n.clone(), v.clone());
                        });
                    })
                    .or_insert(b.clone());
            });
        let mut non_block_entities = CssEntities(
            entities
                .0
                .into_iter()
                .filter(|e| !matches!(e, CssEntity::Block(_)))
                .map(|e| match e {
                    CssEntity::Media(Media { screen, entities }) => Media {
                        screen,
                        entities: self.transform_many(entities),
                    }
                    .into(),
                    CssEntity::Supports(Supports {
                        conditions,
                        entities,
                    }) => Supports {
                        conditions,
                        entities: self.transform_many(entities),
                    }
                    .into(),
                    entity => entity,
                })
                .collect(),
        );
        non_block_entities
            .0
            .append(&mut blocks_to_merge.into_iter().map(|(_, m)| m.into()).collect());
        non_block_entities
    }
}

#[cfg(test)]
mod test {
    use crate::optimizations::merge_blocks::MergeBlocks;
    use crate::optimizations::transformer::Transform;
    use crate::structure::{Block, CssEntities, CssEntity, Selector, SelectorWithPseudoClasses};
    use indexmap::map::IndexMap;

    #[test]
    fn test_blocks() {
        assert_eq!(
            MergeBlocks::default().transform_many(CssEntities(vec![
                CssEntity::Block(Block {
                    selectors: vec![SelectorWithPseudoClasses(
                        Some(Selector::Class("test".into())),
                        vec![]
                    )]
                    .into(),
                    parameters: {
                        let mut tmp = IndexMap::new();
                        tmp.insert("background-color".into(), "#f64e60 !important".into());
                        tmp.into()
                    }
                }),
                CssEntity::Block(Block {
                    selectors: vec![SelectorWithPseudoClasses(
                        Some(Selector::Class("test".into())),
                        vec![]
                    )]
                    .into(),
                    parameters: {
                        let mut tmp = IndexMap::new();
                        tmp.insert("color".into(), "#f64e60 !important".into());
                        tmp.into()
                    }
                }),
            ])),
            CssEntities(vec![CssEntity::Block(Block {
                selectors: vec![SelectorWithPseudoClasses(
                    Some(Selector::Class("test".into())),
                    vec![]
                )]
                .into(),
                parameters: {
                    let mut tmp = IndexMap::new();
                    tmp.insert("background-color".into(), "#f64e60 !important".into());
                    tmp.insert("color".into(), "#f64e60 !important".into());
                    tmp.into()
                }
            })])
        )
    }
}