Skip to main content

turse_core/
lib.rs

1use std::{collections::HashMap, fmt::Display};
2
3pub trait TurseElement {
4    const TAG: &'static str;
5    const ATTRIBUTES: &'static [&'static str];
6}
7
8pub enum Node {
9    Element {
10        tag: String,
11        attrs: HashMap<String, AttrValue>,
12        children: Vec<Node>,
13    },
14    Body(String),
15}
16
17pub trait IntoNode {
18    fn into_inner_node(self) -> Node;
19}
20
21impl IntoNode for String {
22    fn into_inner_node(self) -> Node {
23        Node::Body(self)
24    }
25}
26
27impl IntoNode for &str {
28    fn into_inner_node(self) -> Node {
29        Node::Body(self.to_string())
30    }
31}
32
33impl IntoNode for Node {
34    fn into_inner_node(self) -> Node {
35        self
36    }
37}
38
39impl From<String> for Node {
40    fn from(s: String) -> Self {
41        Node::Body(s)
42    }
43}
44
45impl From<&str> for Node {
46    fn from(s: &str) -> Self {
47        Node::Body(s.to_string())
48    }
49}
50
51impl<T> From<Vec<T>> for Node
52where
53    T: IntoNode,
54{
55    fn from(vec: Vec<T>) -> Self {
56        Node::Element {
57            tag: "fragment".to_string(),
58            attrs: HashMap::new(),
59            children: vec.into_iter().map(|t| t.into_inner_node()).collect(),
60        }
61    }
62}
63
64#[cfg(debug_assertions)]
65impl std::fmt::Debug for Node {
66    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67        match self {
68            Node::Element {
69                tag,
70                attrs,
71                children,
72            } => f
73                .debug_struct("Element")
74                .field("tag", tag)
75                .field("attrs", attrs)
76                .field("children", children)
77                .finish(),
78            Node::Body(s) => f.debug_tuple("Body").field(&s.to_string()).finish(),
79        }
80    }
81}
82
83impl Clone for Node {
84    fn clone(&self) -> Self {
85        match self {
86            Node::Element {
87                tag,
88                attrs,
89                children,
90            } => Node::Element {
91                tag: tag.clone(),
92                attrs: attrs.clone(),
93                children: children.clone(),
94            },
95            Node::Body(s) => Node::Body(s.clone()),
96        }
97    }
98}
99
100#[cfg(debug_assertions)]
101impl PartialEq for Node {
102    fn eq(&self, other: &Self) -> bool {
103        match (self, other) {
104            (
105                Node::Element {
106                    tag: t1,
107                    attrs: a1,
108                    children: c1,
109                },
110                Node::Element {
111                    tag: t2,
112                    attrs: a2,
113                    children: c2,
114                },
115            ) => t1 == t2 && a1 == a2 && c1 == c2,
116            (Node::Body(s1), Node::Body(s2)) => s1 == s2,
117            _ => false,
118        }
119    }
120}
121
122#[cfg_attr(debug_assertions, derive(Debug))]
123#[derive(Clone)]
124pub enum AttrValue {
125    Text(String),
126    Float(f64),
127    Int(i64),
128    Bool(bool),
129    Expr(fn() -> Box<dyn Display>),
130}
131
132#[cfg(debug_assertions)]
133impl PartialEq for AttrValue {
134    fn eq(&self, other: &Self) -> bool {
135        match (self, other) {
136            (Self::Text(lv), Self::Text(rv)) => lv == rv,
137            (Self::Float(lv), Self::Float(rv)) => lv == rv,
138            (Self::Int(lv), Self::Int(rv)) => lv == rv,
139            (Self::Bool(lv), Self::Bool(rv)) => lv == rv,
140            _ => false,
141        }
142    }
143}
144
145impl quote::ToTokens for AttrValue {
146    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
147        match self {
148            AttrValue::Text(s) => quote::quote!(AttrValue::Text(#s.to_string())).to_tokens(tokens),
149            AttrValue::Float(f) => quote::quote!(AttrValue::Float(#f)).to_tokens(tokens),
150            AttrValue::Int(i) => quote::quote!(AttrValue::Int(#i)).to_tokens(tokens),
151            AttrValue::Bool(b) => quote::quote!(AttrValue::Bool(#b)).to_tokens(tokens),
152            AttrValue::Expr(_) => {
153                quote::quote!(AttrValue::Expr(fn() -> Box<dyn Display>)).to_tokens(tokens)
154            }
155        }
156    }
157}
158
159pub trait IntoAttrValue {
160    fn into_attr_value(self) -> AttrValue;
161}
162
163impl<T> IntoAttrValue for T
164where
165    AttrValue: From<T>,
166{
167    fn into_attr_value(self) -> AttrValue {
168        AttrValue::from(self)
169    }
170}
171
172impl From<i64> for AttrValue {
173    fn from(v: i64) -> Self {
174        AttrValue::Int(v)
175    }
176}
177
178impl From<i32> for AttrValue {
179    fn from(v: i32) -> Self {
180        AttrValue::Int(v as i64)
181    }
182}
183
184impl From<i16> for AttrValue {
185    fn from(v: i16) -> Self {
186        AttrValue::Int(v as i64)
187    }
188}
189
190impl From<i8> for AttrValue {
191    fn from(v: i8) -> Self {
192        AttrValue::Int(v as i64)
193    }
194}
195
196impl From<u64> for AttrValue {
197    fn from(v: u64) -> Self {
198        AttrValue::Int(v as i64)
199    }
200}
201
202impl From<u32> for AttrValue {
203    fn from(v: u32) -> Self {
204        AttrValue::Int(v as i64)
205    }
206}
207
208impl From<u16> for AttrValue {
209    fn from(v: u16) -> Self {
210        AttrValue::Int(v as i64)
211    }
212}
213
214impl From<u8> for AttrValue {
215    fn from(v: u8) -> Self {
216        AttrValue::Int(v as i64)
217    }
218}
219
220impl From<f64> for AttrValue {
221    fn from(v: f64) -> Self {
222        AttrValue::Float(v)
223    }
224}
225
226impl From<f32> for AttrValue {
227    fn from(v: f32) -> Self {
228        AttrValue::Float(v as f64)
229    }
230}
231
232impl From<String> for AttrValue {
233    fn from(v: String) -> Self {
234        AttrValue::Text(v)
235    }
236}
237
238impl From<bool> for AttrValue {
239    fn from(v: bool) -> Self {
240        AttrValue::Bool(v)
241    }
242}
243
244impl From<char> for AttrValue {
245    fn from(v: char) -> Self {
246        AttrValue::Text(v.to_string())
247    }
248}
249
250impl From<&str> for AttrValue {
251    fn from(v: &str) -> Self {
252        AttrValue::Text(v.to_string())
253    }
254}
255
256#[cfg_attr(debug_assertions, derive(Debug, PartialEq))]
257#[derive(Clone)]
258pub struct Element {
259    pub inner: Option<Node>,
260}
261
262impl Element {
263    pub fn new(inner: Node) -> Self {
264        Self { inner: Some(inner) }
265    }
266
267    pub fn empty() -> Self {
268        Self { inner: None }
269    }
270}
271
272#[allow(non_camel_case_types)]
273#[allow(non_upper_case_globals)]
274pub mod elements {
275    use super::TurseElement;
276
277    pub struct block;
278    impl TurseElement for block {
279        const TAG: &'static str = "block";
280        const ATTRIBUTES: &'static [&'static str] = &["width"];
281    }
282    pub struct text;
283    impl TurseElement for text {
284        const TAG: &'static str = "text";
285        const ATTRIBUTES: &'static [&'static str] = &["width"];
286    }
287
288    pub struct input;
289    impl TurseElement for input {
290        const TAG: &'static str = "input";
291        const ATTRIBUTES: &'static [&'static str] = &["width"];
292    }
293
294    pub struct dropdown;
295    impl TurseElement for dropdown {
296        const TAG: &'static str = "dropdown";
297        const ATTRIBUTES: &'static [&'static str] = &["width"];
298    }
299}