Skip to main content

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 `vars!` macro for CSS custom properties, the `watch!` macro for
6//! reactive side effects, the `computed!` macro for reactive computed signals,
7//! and the `component` attribute macro.
8
9#[macro_use]
10extern crate syn;
11
12mod class;
13mod computed;
14mod html;
15mod ident;
16mod raw_html;
17mod var;
18mod watch;
19
20pub(crate) use {class::*, computed::*, html::*, ident::*, raw_html::*, var::*, watch::*};
21
22pub(crate) use proc_macro2::TokenTree;
23pub(crate) use std::fmt::{Debug, Formatter};
24
25use std::{
26    collections::{HashMap, hash_map::DefaultHasher},
27    env::{self, VarError},
28    ffi::OsStr,
29    fmt::{self, Write},
30    fs::{create_dir_all, metadata, read_dir, read_to_string, write},
31    hash::{Hash, Hasher},
32    io,
33    iter::Peekable,
34    mem::MaybeUninit,
35    path::PathBuf,
36    time::UNIX_EPOCH,
37};
38
39use {
40    lombok_macros::*,
41    proc_macro::TokenStream,
42    proc_macro2::Span,
43    quote::{ToTokens, quote, quote_spanned},
44    syn::{
45        Attribute, Block, Expr, Field, File, Generics, Ident, Item, LitStr, Path, Stmt, Token,
46        Type, Visibility, WhereClause, braced, parenthesized, parse,
47        parse::{Parse, ParseBuffer, ParseStream},
48        parse_file, parse2,
49        token::{Brace, Colon, Paren, Semi},
50    },
51};
52
53/// The `html!` macro for writing declarative UI in euv.
54///
55/// This macro accepts a syntax similar to Dioxus HTML:
56///
57/// # Arguments
58///
59/// - `TokenStream` - The `html! { ... }` invocation tokens.
60///
61/// # Returns
62///
63/// - `TokenStream` - The expanded virtual-DOM construction tokens.
64#[proc_macro]
65pub fn html(input: TokenStream) -> TokenStream {
66    parse_html(input)
67}
68
69/// The `class!` macro for defining CSS classes with style properties.
70///
71/// Each class definition creates a `Css` function that can be used
72/// in `html!` via the `class:` attribute. Styles are automatically injected
73/// into the DOM on first use.
74///
75/// Parameterized classes keep the normal function-call syntax. Wrap a
76/// parameter reference in `{}` to make it part of the generated class name
77/// by value; parameters referenced without braces keep the default
78/// type-based class name.
79///
80/// # Arguments
81///
82/// - `TokenStream` - The `class! { ... }` invocation tokens.
83///
84/// # Returns
85///
86/// - `TokenStream` - The expanded `Css` function tokens.
87#[proc_macro]
88pub fn class(input: TokenStream) -> TokenStream {
89    parse_class(input)
90}
91
92/// The `watch!` macro for creating reactive side effects.
93///
94/// Watches one or more signals and executes a closure whenever any of them changes.
95/// The closure is also executed once immediately with the current signal values
96/// during initialisation. This initial execution is wrapped in a suppressed-update
97/// scope so that any `.set()` calls inside the body do not trigger unnecessary
98/// DynamicNode re-renders.
99///
100/// The number of signal expressions must match the number of closure parameters.
101/// Each closure parameter receives the current value (via `.get()`) of the
102/// corresponding signal. Parameter types are optional and can be annotated
103/// after a colon.
104///
105/// # Arguments
106///
107/// - `TokenStream` - The `watch! { ... }` invocation tokens.
108///
109/// # Returns
110///
111/// - `TokenStream` - The expanded reactive-effect tokens.
112#[proc_macro]
113pub fn watch(input: TokenStream) -> TokenStream {
114    parse_watch(input)
115}
116
117/// The `computed!` macro for creating reactive computed signals.
118///
119/// Watches one or more signals and derives a new signal whose value is
120/// automatically computed from the closure return value whenever any input
121/// signal changes. The closure must return a value of the specified return type.
122///
123/// The number of signal expressions must match the number of closure parameters.
124/// Each closure parameter receives the current value (via `.get()`) of the
125/// corresponding signal. Parameter types are optional and can be annotated
126/// after a colon. The return type must be specified after `->`.
127///
128/// The result signal is created via `use_signal` and updated via `set()`
129/// to mark its dependents dirty precisely. The initial value is computed immediately
130/// during first render.
131///
132/// # Arguments
133///
134/// - `TokenStream` - The `computed! { ... }` invocation tokens.
135///
136/// # Returns
137///
138/// - `TokenStream` - The expanded computed-signal tokens.
139#[proc_macro]
140pub fn computed(input: TokenStream) -> TokenStream {
141    parse_computed(input)
142}
143
144/// The `vars!` macro for defining CSS custom properties.
145///
146/// Each variable block creates a `Css` function that, when called,
147/// injects the CSS custom properties into the DOM. Variable names are
148/// automatically prefixed with `--`.
149///
150/// Variable names can be written as unquoted kebab-case identifiers
151/// (e.g., `bg-primary`) or as quoted string literals (e.g., `"bg-primary"`).
152///
153/// # Arguments
154///
155/// - `TokenStream` - The `vars! { ... }` invocation tokens.
156///
157/// # Returns
158///
159/// - `TokenStream` - The expanded custom-property injection tokens.
160#[proc_macro]
161pub fn vars(input: TokenStream) -> TokenStream {
162    parse_vars(input)
163}
164
165/// The `var!` macro for referencing CSS custom properties defined via `vars!`.
166///
167/// The variable name can be written as an unquoted kebab-case identifier
168/// (e.g., `bg-primary`) or as a quoted string literal (e.g., `"bg-primary"`),
169/// and expands to the CSS string `"var(--bg-primary)"`.
170///
171/// # Arguments
172///
173/// - `TokenStream` - The `var! { ... }` invocation tokens.
174///
175/// # Returns
176///
177/// - `TokenStream` - The expanded `var(--name)` tokens.
178#[proc_macro]
179pub fn var(input: TokenStream) -> TokenStream {
180    parse_var(input)
181}
182
183/// The `unsafe_no_inline!` macro — escape hatch for raw HTML.
184///
185/// Accepts a string literal and emits a `euv::vdom::RawHtml` value
186/// constructed via `RawHtml::new`. The `unsafe_no_` prefix is a
187/// deliberately loud warning that the string is NOT escaped; treat
188/// it like `Element.innerHTML` in JavaScript.
189///
190/// # Arguments
191///
192/// - `TokenStream` - The `unsafe_no_inline!("...")` invocation tokens.
193///
194/// # Returns
195///
196/// - `TokenStream` - The expanded `RawHtml::new` construction tokens.
197#[proc_macro]
198pub fn unsafe_no_inline(input: TokenStream) -> TokenStream {
199    parse_unsafe_no_inline(input)
200}
201
202/// The `component` attribute macro for marking component functions.
203///
204/// Only functions annotated with `#[component]` are treated as components
205/// in the `html!` macro. All other identifier tags are treated as native
206/// HTML elements (with `Tag::Element`).
207///
208/// The `html!` macro scans the project source to find `#[component]`-annotated
209/// functions at compile time, so this attribute must be present for the
210/// `html!` macro to generate a component function call.
211///
212/// # Arguments
213///
214/// - `TokenStream` - The attribute arguments (unused).
215/// - `TokenStream` - The item being annotated (passed through unchanged).
216///
217/// # Returns
218///
219/// - `TokenStream` - The original item unchanged.
220#[proc_macro_attribute]
221pub fn component(_attr: TokenStream, item: TokenStream) -> TokenStream {
222    item
223}