Skip to main content

velox_dom/
lib.rs

1use std::collections::HashMap;
2
3#[derive(Debug, Clone, PartialEq)]
4pub enum VNode {
5    Element {
6        tag: String,
7        props: Props,
8        children: Vec<VNode>,
9    },
10    Text(String),
11}
12
13#[derive(Debug, Clone, PartialEq, Default)]
14pub struct Props {
15    pub attrs: HashMap<String, String>,
16}
17
18impl Props {
19    pub fn new() -> Self {
20        Self {
21            attrs: HashMap::new(),
22        }
23    }
24    pub fn set(mut self, k: impl Into<String>, v: impl Into<String>) -> Self {
25        self.attrs.insert(k.into(), v.into());
26        self
27    }
28    pub fn from_inline(style: impl Into<String>) -> Self {
29        let mut p = Self::new();
30        p.attrs.insert("style".to_string(), style.into());
31        p
32    }
33    pub fn from_class(class: impl Into<String>) -> Self {
34        let mut p = Self::new();
35        p.attrs.insert("class".to_string(), class.into());
36        p
37    }
38}
39
40// Allow concise props creation
41impl From<()> for Props {
42    fn from(_: ()) -> Self {
43        Props::default()
44    }
45}
46impl From<Vec<(&str, &str)>> for Props {
47    fn from(v: Vec<(&str, &str)>) -> Self {
48        let mut p = Props::new();
49        for (k, v) in v {
50            p.attrs.insert(k.to_string(), v.to_string());
51        }
52        p
53    }
54}
55
56pub fn h(tag: impl Into<String>, props: impl Into<Props>, children: Vec<VNode>) -> VNode {
57    VNode::Element {
58        tag: tag.into(),
59        props: props.into(),
60        children,
61    }
62}
63pub fn text(t: impl Into<String>) -> VNode {
64    VNode::Text(t.into())
65}
66
67pub mod diff;
68
69pub mod layout;
70
71pub mod style;
72
73pub mod text_wrap;
74
75pub use style::*;
76
77/// Unified error type for all Velox operations.
78///
79/// This enum provides structured error handling across the Velox framework,
80/// replacing ad-hoc `String` errors with typed, matchable variants.
81#[derive(Debug, Clone)]
82pub enum VeloxError {
83    /// SFC template parsing failed
84    SfcParse(String),
85    /// CSS stylesheet parsing failed
86    CssParse(String),
87    /// Template code generation failed
88    Codegen(String),
89    /// Layout computation failed
90    Layout(String),
91    /// Rendering backend error (Skia, EGL, softbuffer)
92    Render(String),
93    /// Window creation or management error
94    Window(String),
95    /// Build/Cargo invocation failed
96    Build(String),
97    /// File I/O error
98    Io(String),
99    /// Feature not enabled (e.g., skia-native)
100    FeatureNotEnabled(String),
101    /// Generic internal error
102    Internal(String),
103}
104
105impl std::fmt::Display for VeloxError {
106    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107        match self {
108            Self::SfcParse(msg) => write!(f, "SFC parse error: {msg}"),
109            Self::CssParse(msg) => write!(f, "CSS parse error: {msg}"),
110            Self::Codegen(msg) => write!(f, "Code generation error: {msg}"),
111            Self::Layout(msg) => write!(f, "Layout error: {msg}"),
112            Self::Render(msg) => write!(f, "Render error: {msg}"),
113            Self::Window(msg) => write!(f, "Window error: {msg}"),
114            Self::Build(msg) => write!(f, "Build error: {msg}"),
115            Self::Io(msg) => write!(f, "I/O error: {msg}"),
116            Self::FeatureNotEnabled(msg) => write!(f, "Feature not enabled: {msg}"),
117            Self::Internal(msg) => write!(f, "Internal error: {msg}"),
118        }
119    }
120}
121
122impl std::error::Error for VeloxError {}
123
124impl From<std::io::Error> for VeloxError {
125    fn from(e: std::io::Error) -> Self {
126        Self::Io(e.to_string())
127    }
128}
129
130impl From<String> for VeloxError {
131    fn from(msg: String) -> Self {
132        Self::Internal(msg)
133    }
134}
135
136impl From<&str> for VeloxError {
137    fn from(msg: &str) -> Self {
138        Self::Internal(msg.to_string())
139    }
140}
141
142/// Convenience result type for Velox operations.
143pub type Result<T> = std::result::Result<T, VeloxError>;
144
145#[cfg(test)]
146mod tests {
147    use super::*;
148
149    #[test]
150    fn build_tree() {
151        let node = h(
152            "div",
153            vec![("class", "app")],
154            vec![text("hello"), h("span", (), vec![text("world")])],
155        );
156        if let VNode::Element {
157            tag,
158            props,
159            children,
160        } = node
161        {
162            assert_eq!(tag, "div");
163            assert_eq!(props.attrs.get("class").unwrap(), "app");
164            assert_eq!(children.len(), 2);
165        } else {
166            panic!("expected element");
167        }
168    }
169}