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
#![recursion_limit = "256"]
#![cfg_attr(feature = "cargo-clippy", feature(tool_lints))]
#![cfg_attr(feature = "cargo-clippy", warn(clippy::all))]
//! The crate which removes most of the boilerplate from Ruukh apps.
//!
//! This lib defines `#[component]`, `#[derive(Lifecycle)]` and `html!` macros.
extern crate proc_macro;
use crate::{component::ComponentMeta, html::HtmlRoot};
use proc_macro2::Span;
use quote::quote;
use syn::{parse::Error, parse_macro_input, spanned::Spanned, DeriveInput, Item};
mod component;
mod html;
mod suffix;
/// A convenient auto derive for `Lifecycle` trait. It could be simply written
/// as `impl Lifecycle for MyComponent {}` instead, but why not save some chars.
/// You may use it like:
///
/// # Example
/// ```ignore,compile_fail
/// #[derive(Lifecycle)]
/// struct Button;
/// ```
#[proc_macro_derive(Lifecycle)]
pub fn derive_lifecycle(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let ident = input.ident;
let expanded = quote! {
impl Lifecycle for #ident {}
};
expanded.into()
}
/// `#[component]` macro to derive `Component` trait as well as to do
/// modifications to the struct. It does all the heavy lifting which the user
/// would have to do, to make the component work.
///
/// # Example
/// ```ignore,compile_fail
/// #[component]
/// struct MyButton {
/// disabled: bool,
/// }
/// ```
///
/// You may declare events available on the component by placing `#[events]`
/// attribute. Like:
/// # Example
/// ```ignore,compile_fail
/// #[component]
/// #[events(
/// fn event_name(&self, arg: type) -> return_type;
/// )]
/// struct MyButton {
/// disabled: bool,
/// }
/// ```
/// You may place multiple event declarations. Also, these event declarations
/// require that the event handlers be passed compulsorily.
///
/// `#[component]` also allows to annotate the struct fields with additional
/// attributes. Such as:
///
/// 1. `#[prop]` attribute: This attribute defines that a field is a prop
/// field, though this attribute is optional. This attribute allows passing a
/// default value for the prop field. Like `#[prop(default)]` which delegates
/// it to the types `Default` implementation or `#[prop(default = val)]` which
/// uses the `val` as its default value.
/// Any fields which has a given `default` value or any `Option` type is
/// optional while passing props.
///
/// 2. `#[state]` attribute: This attributes is required to define a field as a
/// state field. If a `#[state]` or `#[state(default)]` is specified then the
/// `Default` value of the field is used. If you want to provide a more
/// specific value, then pass it by using `#[state(default = val)]` attribute.
#[proc_macro_attribute]
#[cfg_attr(
feature = "cargo-clippy",
allow(clippy::needless_pass_by_value)
)]
pub fn component(
metadata: proc_macro::TokenStream,
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
if !metadata.is_empty() {
return Error::new(
Span::call_site(),
"`#[component]` does not support attribute arguments.",
).to_compile_error()
.into();
}
let input = parse_macro_input!(input as Item);
let expanded = match input {
Item::Struct(struct_) => ComponentMeta::parse(struct_)
.map(|s| s.expand())
.unwrap_or_else(|e| e.to_compile_error()),
_ => {
Error::new(input.span(), "Only structs are allowed to be Component").to_compile_error()
}
};
expanded.into()
}
/// `html!` macro to parse `vue`-inspired syntax to generate Markup.
///
/// The basics of using html! macro:
///
/// ## Text
/// ```ignore,compile_fail
/// html! {
/// "This is a sample text."
/// }
/// ```
///
/// ## Empty Markup
/// ```ignore,compile_fail
/// html!()
/// ```
///
/// ## Self-closing tags
/// Only html specified self-closing tags can be self-closing tags.
///
/// ```ignore,compile_fail
/// html! {
/// <br>
/// }
/// ```
///
/// ## Normal tags
/// ```ignore,compile_fail
/// html! {
/// <div></div>
/// <my-custom-tag></my-custom-tag>
/// }
/// ```
///
/// ## Component tags
/// ```ignore,compile_fail
/// html! {
/// <MyComponent></MyComponent>
/// }
/// ```
///
/// ## List of tags
/// ```ignore,compile_fail
/// html! {
/// <div></div>
/// <span></span>
/// <button></button>
/// }
/// ```
///
/// ## Nested markup
/// ```ignore,compile_fail
/// html! {
/// <div>
/// <span></span>
/// </div>
/// }
/// ```
///
/// ## Expressions in between
/// ```ignore,compile_fail
/// html! {
/// "There are "{ count }" people."
/// }
/// ```
#[proc_macro]
pub fn html(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let parsed = parse_macro_input!(input as HtmlRoot);
parsed.expand().into()
}