euv_macros/lib.rs
1//! euv_macros
2//!
3//! Procedural macros for the euv UI framework, including the `html!` macro
4//! for declarative UI syntax, the `class!` macro for CSS class definitions,
5//! the `css_vars!` macro for CSS custom properties, the `watch!` macro for
6//! reactive side effects, and the `component` attribute macro.
7
8mod class;
9mod html;
10mod kebab;
11mod var;
12mod watch;
13
14pub(crate) use {class::*, html::*, kebab::*, var::*, watch::*};
15
16use std::{
17 collections::HashMap,
18 env,
19 ffi::OsStr,
20 fs::{read_dir, read_to_string},
21 iter::Peekable,
22 mem::MaybeUninit,
23 path::PathBuf,
24};
25
26use {
27 lombok_macros::*,
28 proc_macro::TokenStream,
29 proc_macro2::Span,
30 quote::{ToTokens, quote, quote_spanned},
31 syn::{
32 Attribute, Expr, ExprLit, Field, File, Ident, Item, Lit, LitStr, Path, Stmt, Token, Type,
33 Visibility, braced, parenthesized, parse,
34 parse::{Parse, ParseBuffer, ParseStream},
35 parse_file, parse2,
36 token::{Brace, Colon, Paren, Semi},
37 },
38};
39
40/// The `html!` macro for writing declarative UI in euv.
41///
42/// This macro accepts a syntax similar to Dioxus HTML:
43///
44/// ```ignore
45/// html! {
46/// div {
47/// class: c_container()
48/// h1 { "Hello, euv!" }
49/// button {
50/// onclick: move |_| { /* handle click */ },
51/// "Click me"
52/// }
53/// }
54/// }
55/// ```
56#[proc_macro]
57pub fn html(input: TokenStream) -> TokenStream {
58 html::parse_html(input)
59}
60
61/// The `class!` macro for defining CSS classes with style properties.
62///
63/// Each class definition creates a `Css` function that can be used
64/// in `html!` via the `class:` attribute. Styles are automatically injected
65/// into the DOM on first use.
66///
67/// ```ignore
68/// class! {
69/// pub container {
70/// max_width: "800px";
71/// margin: "0 auto";
72/// }
73/// pub(crate) header {
74/// font_size: "28px";
75/// }
76/// hidden {
77/// display: "none";
78/// }
79/// }
80/// ```
81#[proc_macro]
82pub fn class(input: TokenStream) -> TokenStream {
83 class::parse_class(input)
84}
85
86/// The `watch!` macro for creating reactive side effects.
87///
88/// Watches one or more signals and executes a closure whenever any of them changes.
89/// The closure is also executed once immediately with the current signal values
90/// during initialisation. This initial execution is wrapped in a suppressed-update
91/// scope so that any `.set()` calls inside the body do not trigger unnecessary
92/// DynamicNode re-renders.
93///
94/// The number of signal expressions must match the number of closure parameters.
95/// Each closure parameter receives the current value (via `.get()`) of the
96/// corresponding signal.
97///
98/// ```ignore
99/// let count = use_signal(|| 0_i32);
100/// let name = use_signal(|| String::from("euv"));
101/// watch!(count, name, |count_val, name_val| {
102/// web_sys::console::log_1(&format!("count={}, name={}", count_val, name_val).into());
103/// });
104/// ```
105#[proc_macro]
106pub fn watch(input: TokenStream) -> TokenStream {
107 watch::parse_watch(input)
108}
109
110/// The `css_vars!` macro for defining CSS custom properties.
111///
112/// Each variable block creates a `Css` function that, when called,
113/// injects the CSS custom properties into the DOM. Variable names are
114/// automatically prefixed with `--`.
115///
116/// Variable names can be written as unquoted kebab-case identifiers
117/// (e.g., `bg-primary`) or as quoted string literals (e.g., `"bg-primary"`).
118///
119/// ```ignore
120/// css_vars! {
121/// pub c_theme_light {
122/// bg-primary: "#f8f9fb";
123/// text-primary: "#1a1a2e";
124/// }
125/// }
126/// ```
127#[proc_macro]
128pub fn css_vars(input: TokenStream) -> TokenStream {
129 var::parse_css_vars(input)
130}
131
132/// The `var!` macro for referencing CSS custom properties defined via `css_vars!`.
133///
134/// The variable name can be written as an unquoted kebab-case identifier
135/// (e.g., `bg-primary`) or as a quoted string literal (e.g., `"bg-primary"`),
136/// and expands to the CSS string `"var(--bg-primary)"`.
137///
138/// ```ignore
139/// css_vars! {
140/// pub c_theme {
141/// bg-primary: "#f8f9fb";
142/// }
143/// }
144/// class! {
145/// pub c_container {
146/// background: var!(bg-primary);
147/// }
148/// }
149/// ```
150#[proc_macro]
151pub fn var(input: TokenStream) -> TokenStream {
152 var::parse_var(input)
153}
154
155/// The `component` attribute macro for marking component functions.
156///
157/// Only functions annotated with `#[component]` are treated as components
158/// in the `html!` macro. All other identifier tags are treated as native
159/// HTML elements (with `Tag::Element`).
160///
161/// The `html!` macro scans the project source to find `#[component]`-annotated
162/// functions at compile time, so this attribute must be present for the
163/// `html!` macro to generate a component function call.
164///
165/// # Arguments
166///
167/// - `TokenStream` - The attribute arguments (unused).
168/// - `TokenStream` - The item being annotated (passed through unchanged).
169///
170/// # Returns
171///
172/// - `TokenStream` - The original item unchanged.
173#[proc_macro_attribute]
174pub fn component(_attr: TokenStream, item: TokenStream) -> TokenStream {
175 item
176}