forked from DioxusLabs/dioxus
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlib.rs
196 lines (185 loc) · 6.68 KB
/
lib.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
use proc_macro::TokenStream;
use quote::ToTokens;
use rsx::RenderCallBody;
use syn::parse::Parser;
use syn::punctuated::Punctuated;
use syn::{parse_macro_input, Path, Token};
mod component_body;
mod component_body_deserializers;
mod props;
// mod rsx;
use crate::component_body::ComponentBody;
use crate::component_body_deserializers::component::ComponentDeserializerArgs;
use crate::component_body_deserializers::inline_props::InlinePropsDeserializerArgs;
use dioxus_rsx as rsx;
#[proc_macro]
pub fn format_args_f(input: TokenStream) -> TokenStream {
use rsx::*;
format_args_f_impl(parse_macro_input!(input as IfmtInput))
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
#[proc_macro_derive(Props, attributes(props))]
pub fn derive_typed_builder(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as syn::DeriveInput);
match props::impl_my_derive(&input) {
Ok(output) => output.into(),
Err(error) => error.to_compile_error().into(),
}
}
/// The rsx! macro makes it easy for developers to write jsx-style markup in their components.
#[proc_macro]
pub fn rsx(s: TokenStream) -> TokenStream {
match syn::parse::<rsx::CallBody>(s) {
Err(err) => err.to_compile_error().into(),
Ok(body) => body.to_token_stream().into(),
}
}
/// The render! macro makes it easy for developers to write jsx-style markup in their components.
///
/// The render macro automatically renders rsx - making it unhygienic.
#[proc_macro]
pub fn render(s: TokenStream) -> TokenStream {
match syn::parse::<rsx::CallBody>(s) {
Err(err) => err.to_compile_error().into(),
Ok(body) => RenderCallBody(body).into_token_stream().into(),
}
}
/// Derive props for a component within the component definition.
///
/// This macro provides a simple transformation from `Scope<{}>` to `Scope<P>`,
/// removing some boilerplate when defining props.
///
/// You don't *need* to use this macro at all, but it can be helpful in cases where
/// you would be repeating a lot of the usual Rust boilerplate.
///
/// # Example
/// ```rust,ignore
/// #[inline_props]
/// fn app(cx: Scope, bob: String) -> Element {
/// cx.render(rsx!("hello, {bob}"))
/// }
///
/// // is equivalent to
///
/// #[derive(PartialEq, Props)]
/// struct AppProps {
/// bob: String,
/// }
///
/// fn app(cx: Scope<AppProps>) -> Element {
/// cx.render(rsx!("hello, {bob}"))
/// }
/// ```
#[proc_macro_attribute]
#[deprecated(note = "Use `#[component]` instead.")]
pub fn inline_props(_args: TokenStream, s: TokenStream) -> TokenStream {
let comp_body = parse_macro_input!(s as ComponentBody);
match comp_body.deserialize(InlinePropsDeserializerArgs {}) {
Err(e) => e.to_compile_error().into(),
Ok(output) => output.to_token_stream().into(),
}
}
pub(crate) const COMPONENT_ARG_CASE_CHECK_OFF: &str = "no_case_check";
/// Streamlines component creation.
/// This is the recommended way of creating components,
/// though you might want lower-level control with more advanced uses.
///
/// # Arguments
/// * `no_case_check` - Doesn't enforce `PascalCase` on your component names.
/// **This will be removed/deprecated in a future update in favor of a more complete Clippy-backed linting system.**
/// The reasoning behind this is that Clippy allows more robust and powerful lints, whereas
/// macros are extremely limited.
///
/// # Features
/// This attribute:
/// * Enforces that your component uses `PascalCase`.
/// No warnings are generated for the `PascalCase`
/// function name, but everything else will still raise a warning if it's incorrectly `PascalCase`.
/// Does not disable warnings anywhere else, so if you, for example,
/// accidentally don't use `snake_case`
/// for a variable name in the function, the compiler will still warn you.
/// * Automatically uses `#[inline_props]` if there's more than 1 parameter in the function.
/// * Verifies the validity of your component.
/// E.g. if it has a [`Scope`](dioxus_core::Scope) argument.
/// Notes:
/// * This doesn't work 100% of the time, because of macro limitations.
/// * Provides helpful messages if your component is not correct.
/// Possible bugs (please, report these!):
/// * There might be bugs where it incorrectly *denies* validity.
/// This is bad as it means that you can't use the attribute or you have to change the component.
/// * There might be bugs where it incorrectly *confirms* validity.
/// You will still know if the component is invalid once you use it,
/// but the error might be less helpful.
///
/// # Examples
/// * Without props:
/// ```rust,ignore
/// #[component]
/// fn GreetBob(cx: Scope) -> Element {
/// render! { "hello, bob" }
/// }
///
/// // is equivalent to
///
/// #[allow(non_snake_case)]
/// fn GreetBob(cx: Scope) -> Element {
/// #[warn(non_snake_case)]
/// #[inline(always)]
/// fn __dx_inner_comp(cx: Scope) -> Element {
/// render! { "hello, bob" }
/// }
/// // There's no function call overhead since __dx_inner_comp has the #[inline(always)] attribute,
/// // so don't worry about performance.
/// __dx_inner_comp(cx)
/// }
/// ```
/// * With props:
/// ```rust,ignore
/// #[component(no_case_check)]
/// fn GreetPerson(cx: Scope, person: String) -> Element {
/// render! { "hello, {person}" }
/// }
///
/// // is equivalent to
///
/// #[derive(Props, PartialEq)]
/// #[allow(non_camel_case_types)]
/// struct GreetPersonProps {
/// person: String,
/// }
///
/// #[allow(non_snake_case)]
/// fn GreetPerson<'a>(cx: Scope<'a, GreetPersonProps>) -> Element {
/// #[warn(non_snake_case)]
/// #[inline(always)]
/// fn __dx_inner_comp<'a>(cx: Scope<'a, GreetPersonProps>e) -> Element {
/// let GreetPersonProps { person } = &cx.props;
/// {
/// render! { "hello, {person}" }
/// }
/// }
///
/// __dx_inner_comp(cx)
/// }
/// ```
// TODO: Maybe add an option to input a custom component name through the args.
// I think that's unnecessary, but there might be some scenario where it could be useful.
#[proc_macro_attribute]
pub fn component(args: TokenStream, input: TokenStream) -> TokenStream {
let component_body = parse_macro_input!(input as ComponentBody);
let case_check = match Punctuated::<Path, Token![,]>::parse_terminated.parse(args) {
Err(e) => return e.to_compile_error().into(),
Ok(args) => {
if let Some(first) = args.first() {
!first.is_ident(COMPONENT_ARG_CASE_CHECK_OFF)
} else {
true
}
}
};
match component_body.deserialize(ComponentDeserializerArgs { case_check }) {
Err(e) => e.to_compile_error().into(),
Ok(output) => output.to_token_stream().into(),
}
}