hypertext_garnish 0.3.0

Serialization/deserialization of garnish data to HTML/CSS.
Documentation
use serde::Deserialize;

use garnish_lang::compiler::lex::lex;
use garnish_lang::compiler::parse::parse;
use garnish_lang::compiler::build::build_with_data;
use garnish_lang::simple::{SimpleGarnishRuntime, SimpleGarnishData, SimpleRuntimeState};
use garnish_lang::{EmptyContext, GarnishData, GarnishRuntime};
use serde_garnish::GarnishDataDeserializer;

use crate::css::RuleSet;
use crate::html::*;

pub fn make_html_from_garnish(input: &str) -> Result<Node, String> {
    let tokens = lex(input)?;
    let parsed = parse(&tokens)?;
    let mut data = SimpleGarnishData::new();
    build_with_data(parsed.get_root(), parsed.get_nodes().clone(), &mut data)?;
    let mut runtime = SimpleGarnishRuntime::new(data);
    runtime.get_data_mut().push_value_stack(0)?;

    loop {
        match runtime.execute_current_instruction::<EmptyContext>(None) {
            Err(e) => Err(e)?,
            Ok(data) => match data.get_state() {
                SimpleRuntimeState::Running => (),
                SimpleRuntimeState::End => break,
            },
        }
    }

    let mut deserializer = GarnishDataDeserializer::new(runtime.get_data_mut());

    let result = Node::deserialize(&mut deserializer).map_err(|e| e.to_string())?;

    return Ok(result);
}

pub fn make_css_from_garnish(input: &str) -> Result<RuleSet, String> {
    let tokens = lex(input)?;
    let parsed = parse(&tokens)?;
    let mut data = SimpleGarnishData::new();
    build_with_data(parsed.get_root(), parsed.get_nodes().clone(), &mut data)?;
    let mut runtime = SimpleGarnishRuntime::new(data);
    runtime.get_data_mut().push_value_stack(0)?;

    loop {
        match runtime.execute_current_instruction::<EmptyContext>(None) {
            Err(e) => Err(e)?,
            Ok(data) => match data.get_state() {
                SimpleRuntimeState::Running => (),
                SimpleRuntimeState::End => break,
            },
        }
    }

    let mut deserializer = GarnishDataDeserializer::new(runtime.get_data_mut());

    let result = RuleSet::deserialize(&mut deserializer).map_err(|e| match e.message() {
        Some(m) => m.clone(),
        None => e.to_string(),
    })?;

    return Ok(result);
}

#[cfg(test)]
mod test {
    use crate::css::{
        Declaration, DeclarationValue, MediaCondition, MediaConstraint, MediaFeature, MediaQuery,
        Rule, RuleSet, Selector,
    };
    use crate::html::Node;
    use crate::{make_css_from_garnish, make_html_from_garnish};

    #[test]
    fn make_node() {
        let input = ";Node::Text, \"This is a text node\"";
        let output = make_html_from_garnish(input).unwrap();

        assert_eq!(output, Node::Text("This is a text node".to_string()))
    }

    #[test]
    fn make_rule_set() {
        let input = "
;rules = (
    (
        ;selector = (;Selector::Tag \"body\"),
        ;declarations = (
            (
                ;property = \"color\",
                ;value = (;DeclarationValue::Basic \"blue\")
            ),
        )
    ),
),";
        let output = make_css_from_garnish(input).unwrap();

        assert_eq!(
            output,
            RuleSet::new(
                vec![Rule::new(
                    Selector::Tag("body".to_string()),
                    vec![Declaration::new(
                        "color".to_string(),
                        DeclarationValue::Basic("blue".to_string())
                    )],
                    vec![]
                )],
                vec![],
                None
            )
        )
    }

    #[test]
    fn make_rule_set_all_fields() {
        let input = "
;media_query = (
    ;media_type = \"screen\",
    ;constraint = ;MediaConstraint::Only,
    ;features = (
        (
            ;MediaCondition::And
            (
                (;property = \"max-width\" ;value = \"1000px\"),
                (;property = \"orientation\" ;value = \"landscape\")
            )
        ),
    ),
),
;rules = (
    (
        ;selector = (;Selector::Tag \"body\"),
        ;declarations = (
            (
                ;property = \"color\",
                ;value = (;DeclarationValue::Basic \"blue\")
            ),
        ),
        ;sub_rules = (
            (
                ;selector = (;Selector::Tag \"h1\"),
                ;declarations = (
                    (
                        ;property = \"color\",
                        ;value = (;DeclarationValue::Basic \"red\")
                    ),
                ),
            ),
        )
    ),
),
;sub_sets = (
    (
        ;media_query = (
            ;media_type = \"print\",
            ;constraint = ;MediaConstraint::Not,
            ;features = (
                (
                    ;MediaCondition::Or
                    (
                        (;property = \"max-width\" ;value = \"1000px\"),
                        (;property = \"orientation\" ;value = \"landscape\")
                    )
                ),
            )
        ),
        ;rules = (
            (
                ;selector = (;Selector::Tag \"body\"),
                ;declarations = (
                    (
                        ;property = \"color\",
                        ;value = (;DeclarationValue::Basic \"green\")
                    ),
                )
            ),
        ),
    ),
)
";
        let output = make_css_from_garnish(input).unwrap();

        assert_eq!(
            output,
            RuleSet::new(
                vec![Rule::new(
                    Selector::Tag("body".to_string()),
                    vec![Declaration::new(
                        "color".to_string(),
                        DeclarationValue::Basic("blue".to_string())
                    )],
                    vec![Rule::new(
                        Selector::Tag("h1".to_string()),
                        vec![Declaration::new(
                            "color".to_string(),
                            DeclarationValue::Basic("red".to_string())
                        )],
                        vec![]
                    )]
                )],
                vec![RuleSet::new(
                    vec![Rule::new(
                        Selector::Tag("body".to_string()),
                        vec![Declaration::new(
                            "color".to_string(),
                            DeclarationValue::Basic("green".to_string())
                        )],
                        vec![]
                    )],
                    vec![],
                    Some(MediaQuery::new(
                        MediaConstraint::Not,
                        "print".to_string(),
                        vec![MediaCondition::Or(
                            MediaFeature::new("max-width".to_string(), "1000px".to_string()),
                            MediaFeature::new("orientation".to_string(), "landscape".to_string())
                        )]
                    ))
                )],
                Some(MediaQuery::new(
                    MediaConstraint::Only,
                    "screen".to_string(),
                    vec![MediaCondition::And(
                        MediaFeature::new("max-width".to_string(), "1000px".to_string()),
                        MediaFeature::new("orientation".to_string(), "landscape".to_string())
                    )]
                ))
            )
        )
    }
}