Skip to main content

hypen_engine/
lib.rs

1//! # Hypen Engine
2//!
3//! Core reactive rendering engine for the Hypen UI framework.
4//!
5//! This crate provides the platform-agnostic runtime that powers Hypen's
6//! declarative UI model. It parses Hypen DSL, maintains a virtual tree,
7//! tracks reactive dependencies, and emits minimal [`Patch`] operations
8//! when state changes.
9//!
10//! ## Public API
11//!
12//! The primary types for SDK authors and application developers:
13//!
14//! - [`Engine`] — Native Rust engine (for embedding in Rust applications)
15//! - [`EngineError`] — Structured error type for all engine operations
16//! - [`Patch`] — UI mutation operations emitted by the engine
17//! - [`Element`] / [`Value`] — IR building blocks for custom components
18//! - [`Module`] / [`ModuleInstance`] — Stateful module management
19//! - [`StateChange`] — Path-based state change notifications
20//!
21//! For WASM/JavaScript usage, see the [`wasm`] module (enabled via the `js` feature).
22//!
23//! ## Internal Modules
24//!
25//! The following modules are exported for advanced use and testing but are
26//! **not part of the stable API**. Their signatures may change between
27//! minor versions:
28//!
29//! `ir`, `reactive`, `reconcile`, `dispatch`, `render`, `serialize`
30
31pub mod dispatch;
32pub mod engine;
33pub(crate) mod engine_core;
34pub mod error;
35pub mod ir;
36pub mod lifecycle;
37pub mod portable;
38pub mod reactive;
39pub mod reconcile;
40pub mod serialize;
41pub mod state;
42
43/// Internal rendering logic shared between Engine and WasmEngine.
44///
45/// This module is public for integration testing but is not part of the
46/// stable API — use [`Engine`] or `WasmEngine` instead.
47#[doc(hidden)]
48pub mod render;
49
50/// Internal logging utilities.
51#[doc(hidden)]
52pub mod logger;
53
54// WASM bindings module
55// The FFI types (wasm::ffi) are always available for testing and cross-platform use.
56// The actual WASM bindings are conditionally compiled:
57// - `js` feature: JavaScript bindings via wasm-bindgen (Node.js, Bun, browsers)
58// - `wasi` feature: WASI-compatible C FFI (Go, Python, Rust, etc.)
59pub mod wasm;
60
61// UniFFI bindings for native platforms (Kotlin, Swift, Python, Ruby)
62#[cfg(feature = "uniffi")]
63pub mod uniffi;
64
65// UniFFI scaffolding must be in crate root
66#[cfg(feature = "uniffi")]
67::uniffi::setup_scaffolding!();
68
69// ── Public API ─────────────────────────────────────────────────────────
70
71pub use engine::Engine;
72pub use error::EngineError;
73
74pub use ir::{ast_to_ir_node, Element, IRNode, Value};
75pub use ir::{parse_svg, resolve_icons_in_ir, IconData, IconPath, ResourceRegistry};
76pub use lifecycle::{Module, ModuleInstance};
77pub use portable::{
78    build_url, decode_uri_component, diff_paths, encode_uri_component, match_path, parse_query,
79    path_delete, path_get, path_has, path_set, session_step, DiffEntry, RouteMatch, SessionEffect,
80    SessionEvent, SessionPolicy, SessionState,
81};
82pub use reconcile::Patch;
83pub use state::StateChange;
84
85#[cfg(test)]
86mod tailwind_tests {
87    use hypen_tailwind_parse::parse_classes;
88
89    #[test]
90    fn test_tailwind_parse_basic() {
91        let output = parse_classes("p-4 text-blue-500 bg-white");
92        assert_eq!(output.base.len(), 3);
93
94        let props = output.to_props();
95        assert_eq!(props.get("padding"), Some(&"1rem".to_string()));
96        assert_eq!(props.get("color"), Some(&"#3b82f6".to_string()));
97        assert_eq!(props.get("background-color"), Some(&"#ffffff".to_string()));
98    }
99
100    #[test]
101    fn test_tailwind_parse_with_breakpoints() {
102        let output = parse_classes("p-4 md:p-8 lg:p-12");
103
104        let props = output.to_props();
105        assert_eq!(props.get("padding"), Some(&"1rem".to_string()));
106        assert_eq!(props.get("padding@md"), Some(&"2rem".to_string()));
107        assert_eq!(props.get("padding@lg"), Some(&"3rem".to_string()));
108    }
109
110    #[test]
111    fn test_tailwind_parse_with_hover() {
112        let output = parse_classes("bg-white hover:bg-blue-500");
113
114        let props = output.to_props();
115        assert_eq!(props.get("background-color"), Some(&"#ffffff".to_string()));
116        assert_eq!(
117            props.get("background-color:hover"),
118            Some(&"#3b82f6".to_string())
119        );
120    }
121
122    #[test]
123    fn test_tailwind_parse_layout() {
124        let output = parse_classes("flex justify-center items-center gap-4");
125
126        let props = output.to_props();
127        assert_eq!(props.get("display"), Some(&"flex".to_string()));
128        assert_eq!(props.get("justify-content"), Some(&"center".to_string()));
129        assert_eq!(props.get("align-items"), Some(&"center".to_string()));
130        assert_eq!(props.get("gap"), Some(&"1rem".to_string()));
131    }
132
133    #[test]
134    fn test_tailwind_parse_sizing() {
135        let output = parse_classes("w-full h-screen max-w-lg");
136
137        let props = output.to_props();
138        assert_eq!(props.get("width"), Some(&"100%".to_string()));
139        assert_eq!(props.get("height"), Some(&"100vh".to_string()));
140        assert_eq!(props.get("max-width"), Some(&"32rem".to_string()));
141    }
142}