sofie-engine 0.3.0

Screens: the template parser and renderer, and the component catalog.
Documentation
use crate::escape::push_url;
use crate::program::{Program, constant};
use crate::syntax::{Attribute, Expression, Op, Written};

pub(super) const URL_ATTRIBUTES: [&str; 4] = ["href", "src", "action", "formaction"];

impl Program<Written> {
    fn append(&mut self, other: Self) {
        for op in other.into_ops() {
            self.push(op);
        }
    }

    pub fn push_element(&mut self, tag: &str, attributes: Vec<Attribute>, children: Option<Self>) {
        let id = attributes
            .iter()
            .find(|attribute| attribute.name == "id")
            .map(|attribute| attribute.value.clone());
        let mut body = Self::default();
        body.push_markup("<");
        body.push_markup(tag);
        for attribute in attributes {
            body.push_attribute(attribute);
        }
        body.push_markup(">");
        if let Some(children) = children {
            body.append(children);
            body.push_markup("</");
            body.push_markup(tag);
            body.push_markup(">");
        }
        match id {
            Some(id) => self.push(Op::Identified {
                id,
                body: body.into_ops(),
            }),
            None => self.append(body),
        }
    }

    fn push_attribute(&mut self, attribute: Attribute) {
        self.push_markup(" ");
        self.push_markup(&attribute.name);
        self.push_markup("=\"");
        if URL_ATTRIBUTES.contains(&attribute.name.as_str()) {
            self.push_url(attribute.value);
        } else {
            self.push_value(attribute.value);
        }
        self.push_markup("\"");
    }

    fn push_value(&mut self, value: Expression) {
        match value {
            Expression::Authored(markup) => self.push_markup(&markup),
            Expression::Interpolated(parts) => {
                for part in parts {
                    self.push_value(part);
                }
            }
            _ => self.push_text(value),
        }
    }

    fn push_url(&mut self, url: Expression) {
        let is_authored = matches!(url, Expression::Authored(_));
        let Some(text) = constant(&url) else {
            self.push(Op::Url(url));
            return;
        };
        push_url(&mut self.markup, &text, is_authored);
    }
}