lib.rs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. use proc_macro::TokenStream;
  2. use quote::ToTokens;
  3. use syn::parse_macro_input;
  4. pub(crate) mod ifmt;
  5. pub(crate) mod inlineprops;
  6. pub(crate) mod props;
  7. pub(crate) mod router;
  8. pub(crate) mod rsx;
  9. #[proc_macro]
  10. pub fn format_args_f(input: TokenStream) -> TokenStream {
  11. use ifmt::*;
  12. let item = parse_macro_input!(input as IfmtInput);
  13. format_args_f_impl(item)
  14. .unwrap_or_else(|err| err.to_compile_error())
  15. .into()
  16. }
  17. #[proc_macro_derive(Props, attributes(props))]
  18. pub fn derive_typed_builder(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
  19. let input = parse_macro_input!(input as syn::DeriveInput);
  20. match props::impl_my_derive(&input) {
  21. Ok(output) => output.into(),
  22. Err(error) => error.to_compile_error().into(),
  23. }
  24. }
  25. /// The html! macro makes it easy for developers to write jsx-style markup in their components.
  26. ///
  27. /// ## Complete Reference Guide:
  28. /// ```
  29. /// const Example: Component<()> = |cx, props|{
  30. /// let formatting = "formatting!";
  31. /// let formatting_tuple = ("a", "b");
  32. /// let lazy_fmt = format_args!("lazily formatted text");
  33. /// cx.render(rsx! {
  34. /// div {
  35. /// // Elements
  36. /// div {}
  37. /// h1 {"Some text"}
  38. /// h1 {"Some text with {formatting}"}
  39. /// h1 {"Formatting basic expressions {formatting_tuple.0} and {formatting_tuple.1}"}
  40. /// h2 {
  41. /// "Multiple"
  42. /// "Text"
  43. /// "Blocks"
  44. /// "Use comments as separators in html"
  45. /// }
  46. /// div {
  47. /// h1 {"multiple"}
  48. /// h2 {"nested"}
  49. /// h3 {"elements"}
  50. /// }
  51. /// div {
  52. /// class: "my special div"
  53. /// h1 {"Headers and attributes!"}
  54. /// }
  55. /// div {
  56. /// // pass simple rust expressions in
  57. /// class: lazy_fmt,
  58. /// id: format_args!("attributes can be passed lazily with std::fmt::Arguments"),
  59. /// div {
  60. /// class: {
  61. /// const WORD: &str = "expressions";
  62. /// format_args!("Arguments can be passed in through curly braces for complex {}", WORD)
  63. /// }
  64. /// }
  65. /// }
  66. ///
  67. /// // Expressions can be used in element position too:
  68. /// {rsx!(p { "More templating!" })}
  69. /// {html!(<p>"Even HTML templating!!"</p>)}
  70. ///
  71. /// // Iterators
  72. /// {(0..10).map(|i| rsx!(li { "{i}" }))}
  73. /// {{
  74. /// let data = std::collections::HashMap::<&'static str, &'static str>::new();
  75. /// // Iterators *should* have keys when you can provide them.
  76. /// // Keys make your app run faster. Make sure your keys are stable, unique, and predictable.
  77. /// // Using an "ID" associated with your data is a good idea.
  78. /// data.into_iter().map(|(k, v)| rsx!(li { key: "{k}" "{v}" }))
  79. /// }}
  80. ///
  81. /// // Matching
  82. /// {match true {
  83. /// true => rsx!(h1 {"Top text"}),
  84. /// false => rsx!(h1 {"Bottom text"})
  85. /// }}
  86. ///
  87. /// // Conditional rendering
  88. /// // Dioxus conditional rendering is based around None/Some. We have no special syntax for conditionals.
  89. /// // You can convert a bool condition to rsx! with .then and .or
  90. /// {true.then(|| rsx!(div {}))}
  91. ///
  92. /// // True conditions
  93. /// {if true {
  94. /// rsx!(h1 {"Top text"})
  95. /// } else {
  96. /// rsx!(h1 {"Bottom text"})
  97. /// }}
  98. ///
  99. /// // returning "None" is a bit noisy... but rare in practice
  100. /// {None as Option<()>}
  101. ///
  102. /// // Use the Dioxus type-alias for less noise
  103. /// {NONE_ELEMENT}
  104. ///
  105. /// // can also just use empty fragments
  106. /// Fragment {}
  107. ///
  108. /// // Fragments let you insert groups of nodes without a parent.
  109. /// // This lets you make components that insert elements as siblings without a container.
  110. /// div {"A"}
  111. /// Fragment {
  112. /// div {"B"}
  113. /// div {"C"}
  114. /// Fragment {
  115. /// "D"
  116. /// Fragment {
  117. /// "heavily nested fragments is an antipattern"
  118. /// "they cause Dioxus to do unnecessary work"
  119. /// "don't use them carelessly if you can help it"
  120. /// }
  121. /// }
  122. /// }
  123. ///
  124. /// // Components
  125. /// // Can accept any paths
  126. /// // Notice how you still get syntax highlighting and IDE support :)
  127. /// Baller {}
  128. /// baller::Baller { }
  129. /// crate::baller::Baller {}
  130. ///
  131. /// // Can take properties
  132. /// Taller { a: "asd" }
  133. ///
  134. /// // Can take optional properties
  135. /// Taller { a: "asd" }
  136. ///
  137. /// // Can pass in props directly as an expression
  138. /// {{
  139. /// let props = TallerProps {a: "hello"};
  140. /// rsx!(Taller { ..props })
  141. /// }}
  142. ///
  143. /// // Spreading can also be overridden manually
  144. /// Taller {
  145. /// ..TallerProps { a: "ballin!" }
  146. /// a: "not ballin!"
  147. /// }
  148. ///
  149. /// // Can take children too!
  150. /// Taller { a: "asd", div {"hello world!"} }
  151. /// }
  152. /// })
  153. /// };
  154. ///
  155. /// mod baller {
  156. /// use super::*;
  157. /// pub struct BallerProps {}
  158. ///
  159. /// /// This component totally balls
  160. /// pub fn Baller(cx: Context<()>) -> DomTree {
  161. /// todo!()
  162. /// }
  163. /// }
  164. ///
  165. /// #[derive(Debug, PartialEq, Props)]
  166. /// pub struct TallerProps {
  167. /// a: &'static str,
  168. /// }
  169. ///
  170. /// /// This component is taller than most :)
  171. /// pub fn Taller(cx: Context<TallerProps>) -> DomTree {
  172. /// let b = true;
  173. /// todo!()
  174. /// }
  175. /// ```
  176. #[proc_macro]
  177. pub fn rsx(s: TokenStream) -> TokenStream {
  178. match syn::parse::<rsx::CallBody>(s) {
  179. Err(e) => e.to_compile_error().into(),
  180. Ok(s) => s.to_token_stream().into(),
  181. }
  182. }
  183. /// Derive macro used to mark an enum as Routable.
  184. ///
  185. /// This macro can only be used on enums. Every varient of the macro needs to be marked
  186. /// with the `at` attribute to specify the URL of the route. It generates an implementation of
  187. /// `yew_router::Routable` trait and `const`s for the routes passed which are used with `Route`
  188. /// component.
  189. ///
  190. /// # Example
  191. ///
  192. /// ```
  193. /// # use yew_router::Routable;
  194. /// #[derive(Debug, Clone, Copy, PartialEq, Routable)]
  195. /// enum Routes {
  196. /// #[at("/")]
  197. /// Home,
  198. /// #[at("/secure")]
  199. /// Secure,
  200. /// #[at("/profile/{id}")]
  201. /// Profile(u32),
  202. /// #[at("/404")]
  203. /// NotFound,
  204. /// }
  205. /// ```
  206. #[proc_macro_derive(Routable, attributes(at, not_found))]
  207. pub fn routable_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
  208. use router::{routable_derive_impl, Routable};
  209. let input = parse_macro_input!(input as Routable);
  210. routable_derive_impl(input).into()
  211. }
  212. /// Derive props for a component within the component definition.
  213. ///
  214. /// This macro provides a simple transformation from `Scope<{}>` to `Scope<P>`,
  215. /// removing some boilerplate when defining props.
  216. ///
  217. /// You don't *need* to use this macro at all, but it can be helpful in cases where
  218. /// you would be repeating a lot of the usual Rust boilerplate.
  219. ///
  220. /// # Example
  221. /// ```
  222. /// #[inline_props]
  223. /// fn app(cx: Scope<{ bob: String }>) -> Element {
  224. /// cx.render(rsx!("hello, {bob}"))
  225. /// }
  226. ///
  227. /// // is equivalent to
  228. ///
  229. /// #[derive(PartialEq, Props)]
  230. /// struct AppProps {
  231. /// bob: String,
  232. /// }
  233. ///
  234. /// fn app(cx: Scope<AppProps>) -> Element {
  235. /// cx.render(rsx!("hello, {bob}"))
  236. /// }
  237. /// ```
  238. #[proc_macro_attribute]
  239. pub fn inline_props(_args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {
  240. match syn::parse::<inlineprops::InlinePropsBody>(s) {
  241. Err(e) => e.to_compile_error().into(),
  242. Ok(s) => s.to_token_stream().into(),
  243. }
  244. }