turse-core 0.1.1

Core types for turse
Documentation
use std::{collections::HashMap, fmt::Display};

pub trait TurseElement {
    const TAG: &'static str;
    const ATTRIBUTES: &'static [&'static str];
}

pub enum Node {
    Element {
        tag: String,
        attrs: HashMap<String, AttrValue>,
        children: Vec<Node>,
    },
    Body(String),
}

pub trait IntoNode {
    fn into_inner_node(self) -> Node;
}

impl IntoNode for String {
    fn into_inner_node(self) -> Node {
        Node::Body(self)
    }
}

impl IntoNode for &str {
    fn into_inner_node(self) -> Node {
        Node::Body(self.to_string())
    }
}

impl IntoNode for Node {
    fn into_inner_node(self) -> Node {
        self
    }
}

impl From<String> for Node {
    fn from(s: String) -> Self {
        Node::Body(s)
    }
}

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

impl<T> From<Vec<T>> for Node
where
    T: IntoNode,
{
    fn from(vec: Vec<T>) -> Self {
        Node::Element {
            tag: "fragment".to_string(),
            attrs: HashMap::new(),
            children: vec.into_iter().map(|t| t.into_inner_node()).collect(),
        }
    }
}

#[cfg(debug_assertions)]
impl std::fmt::Debug for Node {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Node::Element {
                tag,
                attrs,
                children,
            } => f
                .debug_struct("Element")
                .field("tag", tag)
                .field("attrs", attrs)
                .field("children", children)
                .finish(),
            Node::Body(s) => f.debug_tuple("Body").field(&s.to_string()).finish(),
        }
    }
}

impl Clone for Node {
    fn clone(&self) -> Self {
        match self {
            Node::Element {
                tag,
                attrs,
                children,
            } => Node::Element {
                tag: tag.clone(),
                attrs: attrs.clone(),
                children: children.clone(),
            },
            Node::Body(s) => Node::Body(s.clone()),
        }
    }
}

#[cfg(debug_assertions)]
impl PartialEq for Node {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (
                Node::Element {
                    tag: t1,
                    attrs: a1,
                    children: c1,
                },
                Node::Element {
                    tag: t2,
                    attrs: a2,
                    children: c2,
                },
            ) => t1 == t2 && a1 == a2 && c1 == c2,
            (Node::Body(s1), Node::Body(s2)) => s1 == s2,
            _ => false,
        }
    }
}

#[cfg_attr(debug_assertions, derive(Debug))]
#[derive(Clone)]
pub enum AttrValue {
    Text(String),
    Float(f64),
    Int(i64),
    Bool(bool),
    Expr(fn() -> Box<dyn Display>),
}

#[cfg(debug_assertions)]
impl PartialEq for AttrValue {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (Self::Text(lv), Self::Text(rv)) => lv == rv,
            (Self::Float(lv), Self::Float(rv)) => lv == rv,
            (Self::Int(lv), Self::Int(rv)) => lv == rv,
            (Self::Bool(lv), Self::Bool(rv)) => lv == rv,
            _ => false,
        }
    }
}

impl quote::ToTokens for AttrValue {
    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
        match self {
            AttrValue::Text(s) => quote::quote!(AttrValue::Text(#s.to_string())).to_tokens(tokens),
            AttrValue::Float(f) => quote::quote!(AttrValue::Float(#f)).to_tokens(tokens),
            AttrValue::Int(i) => quote::quote!(AttrValue::Int(#i)).to_tokens(tokens),
            AttrValue::Bool(b) => quote::quote!(AttrValue::Bool(#b)).to_tokens(tokens),
            AttrValue::Expr(_) => {
                quote::quote!(AttrValue::Expr(fn() -> Box<dyn Display>)).to_tokens(tokens)
            }
        }
    }
}

pub trait IntoAttrValue {
    fn into_attr_value(self) -> AttrValue;
}

impl<T> IntoAttrValue for T
where
    AttrValue: From<T>,
{
    fn into_attr_value(self) -> AttrValue {
        AttrValue::from(self)
    }
}

impl From<i64> for AttrValue {
    fn from(v: i64) -> Self {
        AttrValue::Int(v)
    }
}

impl From<i32> for AttrValue {
    fn from(v: i32) -> Self {
        AttrValue::Int(v as i64)
    }
}

impl From<i16> for AttrValue {
    fn from(v: i16) -> Self {
        AttrValue::Int(v as i64)
    }
}

impl From<i8> for AttrValue {
    fn from(v: i8) -> Self {
        AttrValue::Int(v as i64)
    }
}

impl From<u64> for AttrValue {
    fn from(v: u64) -> Self {
        AttrValue::Int(v as i64)
    }
}

impl From<u32> for AttrValue {
    fn from(v: u32) -> Self {
        AttrValue::Int(v as i64)
    }
}

impl From<u16> for AttrValue {
    fn from(v: u16) -> Self {
        AttrValue::Int(v as i64)
    }
}

impl From<u8> for AttrValue {
    fn from(v: u8) -> Self {
        AttrValue::Int(v as i64)
    }
}

impl From<f64> for AttrValue {
    fn from(v: f64) -> Self {
        AttrValue::Float(v)
    }
}

impl From<f32> for AttrValue {
    fn from(v: f32) -> Self {
        AttrValue::Float(v as f64)
    }
}

impl From<String> for AttrValue {
    fn from(v: String) -> Self {
        AttrValue::Text(v)
    }
}

impl From<bool> for AttrValue {
    fn from(v: bool) -> Self {
        AttrValue::Bool(v)
    }
}

impl From<char> for AttrValue {
    fn from(v: char) -> Self {
        AttrValue::Text(v.to_string())
    }
}

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

#[cfg_attr(debug_assertions, derive(Debug, PartialEq))]
#[derive(Clone)]
pub struct Element {
    pub inner: Option<Node>,
}

impl Element {
    pub fn new(inner: Node) -> Self {
        Self { inner: Some(inner) }
    }

    pub fn empty() -> Self {
        Self { inner: None }
    }
}

#[allow(non_camel_case_types)]
#[allow(non_upper_case_globals)]
pub mod elements {
    use super::TurseElement;

    pub struct block;
    impl TurseElement for block {
        const TAG: &'static str = "block";
        const ATTRIBUTES: &'static [&'static str] = &["width"];
    }
    pub struct text;
    impl TurseElement for text {
        const TAG: &'static str = "text";
        const ATTRIBUTES: &'static [&'static str] = &["width"];
    }

    pub struct input;
    impl TurseElement for input {
        const TAG: &'static str = "input";
        const ATTRIBUTES: &'static [&'static str] = &["width"];
    }

    pub struct dropdown;
    impl TurseElement for dropdown {
        const TAG: &'static str = "dropdown";
        const ATTRIBUTES: &'static [&'static str] = &["width"];
    }
}