ratatui_kit_macros/lib.rs
1#![doc = include_str!("../README.md")]
2
3// ratatui-kit-macros:核心 UI 宏定义,简化终端 UI 组件开发。
4//
5// ## 主要宏说明
6//
7// - `#[derive(Props)]`:为组件属性自动生成 Props trait 实现。
8// - `element!`:声明式 UI 宏,极大提升终端 UI 组件开发效率。
9// - 语法风格类似 React JSX,但为 Rust 语法友好设计。
10// - 支持嵌套、props、children、一等控制流渲染。
11// - **一等控制流**:子节点块内可直接写 `if/else`、`if let`、`for`、`match`,分支体即子节点;
12// 各分支独立 extend,可返回不同元素类型,无需 `.into_any()`。
13// - `{ expr }` 可内嵌任意返回 Option/Vec/impl Iterator/Element 的 Rust 表达式。
14// - `widget(expr)` / `stateful(widget, state)` 可兼容 ratatui 原生组件(逃生舱)。
15// - 适用于声明式构建终端 UI 组件树。
16//
17// ## element! 宏语法
18//
19// 例如,声明式构建一个带一等控制流和 ratatui 原生组件的 UI:
20//
21// ```rust,ignore
22// element!(Panel(title: "Demo") {
23// if show_title {
24// Title(text: "Hello")
25// }
26// for item in items {
27// ListItem(label: item, key: item.id)
28// }
29// widget(Block::default().borders(Borders::ALL))
30// })
31// ```
32//
33// - 控制流分支体直接写子元素;动态/复杂表达式仍可用 `{ expr }`。
34// - 通过 `widget(...)` / `stateful(...)` 可直接集成 ratatui 原生组件。
35// - 语法风格类似 JSX,但为 Rust 语法友好设计。
36// - 适用于声明式构建终端 UI 组件树。
37
38use element::ElementOrAdapter;
39use proc_macro::TokenStream;
40use props::ParsedProps;
41use quote::ToTokens;
42use syn::DeriveInput;
43
44use crate::with_layout_style::impl_layout_style;
45
46mod adapter;
47mod component;
48mod element;
49mod props;
50#[cfg(feature = "router")]
51mod router;
52mod utils;
53mod with_layout_style;
54
55/// Derives Ratatui Kit's `Props` implementation for component props.
56#[proc_macro_derive(Props, attributes(layout))]
57pub fn derive_props(item: TokenStream) -> TokenStream {
58 let props = syn::parse_macro_input!(item as ParsedProps);
59 props.to_token_stream().into()
60}
61
62/// Builds a declarative Ratatui Kit element tree.
63///
64/// The syntax is JSX-like while staying Rust-friendly: nested components,
65/// props, children, `key`, first-class `if`/`if let`/`for`/`match` child
66/// control flow, and native Ratatui widget adapters are supported.
67///
68/// ```rust,ignore
69/// element!(Panel(title: "Demo") {
70/// if show_title {
71/// Title(text: "Hello")
72/// } else {
73/// Title(text: "Hidden")
74/// }
75/// for item in items {
76/// ListItem(label: item, key: item.id)
77/// }
78/// widget(Block::default().borders(Borders::ALL))
79/// })
80/// ```
81#[proc_macro]
82pub fn element(input: TokenStream) -> TokenStream {
83 let element = syn::parse_macro_input!(input as ElementOrAdapter);
84 element.to_token_stream().into()
85}
86
87/// Turns a function into a Ratatui Kit component.
88///
89/// The generated component owns the props type, preserves hook order, and
90/// enables context-aware hooks for ordinary function components.
91#[proc_macro_attribute]
92pub fn component(_attr: TokenStream, item: TokenStream) -> TokenStream {
93 let component = syn::parse_macro_input!(item as component::ParsedComponent);
94 component.to_token_stream().into()
95}
96
97/// Builds a static route table for `RouterProvider`.
98///
99/// Routes are matched in declaration order. Put more specific static routes
100/// before same-prefix dynamic routes when both could match the same path.
101#[cfg(feature = "router")]
102#[proc_macro]
103pub fn routes(input: TokenStream) -> TokenStream {
104 let routes = syn::parse_macro_input!(input as router::Routes);
105 routes.to_token_stream().into()
106}
107
108/// Adds layout style fields and helpers to a props struct.
109///
110/// Use this on named-field props structs for components that should accept
111/// layout props such as width, height, margin, offset, gap, direction, and
112/// justification.
113#[proc_macro_attribute]
114pub fn with_layout_style(attr: TokenStream, item: TokenStream) -> TokenStream {
115 let layout_style = syn::parse_macro_input!(attr as with_layout_style::ParsedLayoutStyle);
116 let props = syn::parse_macro_input!(item as DeriveInput);
117 impl_layout_style(&layout_style, props).into()
118}