wasm-css 0.2.0

Ergonomic WASM CSS Framework
Documentation
// Authors: Robert Lopez

use super::{effect::Effect, Components};
use std::collections::BTreeMap;

// TODO: Handle nested effects like: @media (max-width: 800px) { &:hover { ... } }

#[derive(Debug)]
enum State {
    FieldKey,
    EffectKey,
    FieldValue,
    EffectKeyInner,
    EffectValueInner,
}

impl Components {
    pub(crate) fn from_css(css: &str) -> Self {
        let css_bytes = css.as_bytes();
        let mut fields = BTreeMap::new();
        let mut effects = vec![];

        let mut state = None;
        let mut key_buffer = vec![];
        let mut value_buffer = vec![];
        let mut effect_buffer = Effect::new("".to_string());
        let mut previous_byte = None::<u8>;
        let mut key_inner_buffer = vec![];
        let mut value_inner_buffer = vec![];

        for (byte_index, byte) in css.as_bytes().iter().enumerate() {
            match byte {
                b'\n' => {}
                b' ' => {
                    macro_rules! handle_space {
                        ($buffer:expr) => {
                            if !$buffer.is_empty() {
                                let previous_byte = previous_byte.unwrap();
                                if previous_byte != b' '
                                    && previous_byte != b'('
                                    && !previous_byte.is_ascii_digit()
                                    && !css_bytes.get(byte_index + 1).is_some_and(|b| b == &b')')
                                {
                                    $buffer.push(*byte);
                                }
                            }
                        };
                    }

                    match state {
                        Some(State::EffectKey) => {
                            handle_space!(&mut key_buffer);
                        }
                        Some(State::FieldValue) => {
                            handle_space!(&mut value_buffer);
                        }
                        Some(State::EffectValueInner) => {
                            handle_space!(&mut value_inner_buffer);
                        }
                        _ => {}
                    }
                }
                b'&' | b'@' => {
                    state = Some(State::EffectKey);

                    key_buffer.push(*byte);
                }
                b'{' => {
                    state = Some(State::EffectKeyInner);
                }
                b':' => match state {
                    Some(State::FieldKey) => {
                        state = Some(State::FieldValue);
                    }
                    Some(State::EffectKey) => {
                        key_buffer.push(*byte);
                    }
                    Some(State::EffectKeyInner) => {
                        state = Some(State::EffectValueInner);
                    }
                    _ => {
                        // TODO
                    }
                },
                b';' => match state {
                    Some(State::FieldValue) => {
                        let mut key_bytes = vec![b'\t'];
                        key_bytes.append(&mut key_buffer);

                        match state {
                            Some(State::FieldValue) => {
                                let mut value_bytes = vec![];
                                value_bytes.append(&mut value_buffer);

                                key_bytes.extend_from_slice(b": ");
                                value_bytes.extend_from_slice(b";\n");

                                unsafe {
                                    fields.insert(
                                        String::from_utf8_unchecked(key_bytes),
                                        String::from_utf8_unchecked(value_bytes),
                                    );
                                }
                            }
                            _ => {
                                // TODO
                            }
                        }

                        state = None;
                    }
                    Some(State::EffectValueInner) => {
                        let mut effect_key_bytes = vec![b'\t'; 2];
                        effect_key_bytes.append(&mut key_inner_buffer);
                        effect_key_bytes.extend_from_slice(b": ");

                        let mut effect_value_bytes = vec![];
                        effect_value_bytes.append(&mut value_inner_buffer);
                        effect_value_bytes.extend_from_slice(b";\n");

                        unsafe {
                            effect_buffer.fields.insert(
                                String::from_utf8_unchecked(effect_key_bytes),
                                String::from_utf8_unchecked(effect_value_bytes),
                            );
                        }

                        state = Some(State::EffectKeyInner);
                    }
                    _ => {
                        // TODO
                    }
                },
                b'}' => match state {
                    Some(State::EffectKeyInner) => {
                        let mut key_bytes = vec![b'\t'];
                        key_bytes.append(&mut key_buffer);

                        match state {
                            Some(State::EffectKeyInner) => {
                                key_bytes.extend_from_slice(b"{\n");

                                let mut effect =
                                    Effect::new(unsafe { String::from_utf8_unchecked(key_bytes) });
                                std::mem::swap(&mut effect.fields, &mut effect_buffer.fields);

                                effects.push(effect);
                            }
                            _ => {
                                // TODO
                            }
                        }

                        state = None;
                    }
                    _ => {
                        // TODO
                    }
                },
                byte => match state {
                    Some(State::FieldKey) | Some(State::EffectKey) => {
                        key_buffer.push(*byte);
                    }
                    Some(State::FieldValue) => {
                        value_buffer.push(*byte);
                    }
                    Some(State::EffectKeyInner) => {
                        key_inner_buffer.push(*byte);
                    }
                    Some(State::EffectValueInner) => {
                        value_inner_buffer.push(*byte);
                    }
                    None => {
                        state = Some(State::FieldKey);

                        key_buffer.push(*byte);
                    }
                },
            }

            previous_byte = Some(*byte);
        }

        Self { fields, effects }
    }
}