simple-rsx 0.1.3

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

pub enum NodeList {
    Fragment(Vec<Node>),
    Single(Node),
}

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

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

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

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

impl From<Node> for NodeList {
    fn from(node: Node) -> Self {
        NodeList::Single(node)
    }
}

impl From<Vec<Node>> for NodeList {
    fn from(nodes: Vec<Node>) -> Self {
        NodeList::Fragment(nodes)
    }
}

impl From<Vec<NodeList>> for NodeList {
    fn from(nodes: Vec<NodeList>) -> Self {
        let mut result = Vec::new();
        for node in nodes {
            match node {
                NodeList::Fragment(nodes) => {
                    result.extend(nodes);
                }
                NodeList::Single(node) => {
                    result.push(node);
                }
            }
        }
        NodeList::Fragment(result)
    }
}

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

impl Display for NodeList {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            NodeList::Fragment(nodes) => {
                for node in nodes {
                    write!(f, "{}", node)?;
                }
                Ok(())
            }
            NodeList::Single(node) => {
                write!(f, "{}", node)?;
                Ok(())
            }
        }
    }
}

pub enum Node {
    Element(Element),
    Text(String),
}

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(())
            }
        }
    }
}

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())
    }
}