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