simple-rsx 0.1.4

A simple JSX-like syntax implementation for Rust
Documentation
pub use simple_rsx_macros::rsx;
use std::{collections::HashMap, fmt::Display};

pub enum Node {
    Element(Element),
    Text(String),
    Fragment(Vec<Node>),
}

impl From<String> for Node {
    fn from(value: String) -> Self {
        Node::Text(value.to_string())
    }
}

impl From<&str> for Node {
    fn from(value: &str) -> Self {
        Node::Text(value.to_string())
    }
}

impl From<&&str> for Node {
    fn from(value: &&str) -> Self {
        Node::Text(value.to_string())
    }
}

impl<T: ToString> From<Vec<T>> for Node {
    fn from(value: Vec<T>) -> Self {
        Node::Fragment(
            value
                .into_iter()
                .map(|t| Node::Text(t.to_string()))
                .collect(),
        )
    }
}

impl From<i32> for Node {
    fn from(value: i32) -> Self {
        Node::Text(value.to_string())
    }
}

impl FromIterator<i32> for Node {
    fn from_iter<T: IntoIterator<Item = i32>>(iter: T) -> Self {
        let mut result = Vec::new();
        for i in iter {
            result.push(Node::Text(i.to_string()));
        }
        Node::Fragment(result)
    }
}

impl From<f32> for Node {
    fn from(value: f32) -> Self {
        Node::Text(value.to_string())
    }
}

impl From<bool> for Node {
    fn from(value: bool) -> Self {
        Node::Text(value.to_string())
    }
}

impl<I, F, R> From<std::iter::Map<I, F>> for Node
where
    I: Iterator,
    F: FnMut(I::Item) -> R,
    R: Into<Node>,
    Vec<Node>: FromIterator<R>,
{
    fn from(iter: std::iter::Map<I, F>) -> Self {
        let nodes: Vec<Node> = iter.collect();
        Node::from(nodes)
    }
}

impl Display for Node {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Node::Element(el) => {
                write!(f, "<{}", el.tag)?;
                for (key, value) in &el.attributes {
                    write!(f, " {}=\"{}\"", key, value)?;
                }
                write!(f, ">")?;
                for child in &el.children {
                    write!(f, "{}", child)?;
                }
                write!(f, "</{}>", el.tag)?;
                Ok(())
            }
            Node::Text(text) => {
                write!(f, "{}", text)?;
                Ok(())
            }
            Node::Fragment(nodes) => {
                for node in nodes {
                    write!(f, "{}", node)?;
                }
                Ok(())
            }
        }
    }
}

pub trait NodeValue {
    fn value(&self) -> String;
}

impl<T: ToString> NodeValue for T {
    fn value(&self) -> String {
        self.to_string()
    }
}

pub struct Element {
    tag: String,
    attributes: HashMap<String, String>,
    children: Vec<Node>,
}

impl Element {
    pub fn new(tag: &str) -> Node {
        Node::Element(Element {
            tag: tag.to_string(),
            attributes: HashMap::new(),
            children: Vec::new(),
        })
    }

    pub fn set_attribute(&mut self, name: &str, value: impl NodeValue) {
        self.attributes.insert(name.to_string(), value.value());
    }

    pub fn append_child(&mut self, node: Node) {
        self.children.push(node);
    }
}

impl Node {
    pub fn as_element_mut(&mut self) -> Option<&mut Element> {
        match self {
            Node::Element(el) => Some(el),
            _ => None,
        }
    }

    pub fn append_child(&mut self, node: Node) {
        if let Node::Element(el) = self {
            el.children.push(node);
        }
    }
}

pub struct TextNode;

impl TextNode {
    pub fn new(text: &str) -> Node {
        Node::Text(text.to_string())
    }
}