Skip to content

Commit

Permalink
♻️ Rewrite the macros structure.
Browse files Browse the repository at this point in the history
  • Loading branch information
langyo committed Dec 18, 2023
1 parent 8530c23 commit 0f0f21d
Show file tree
Hide file tree
Showing 9 changed files with 250 additions and 135 deletions.
3 changes: 3 additions & 0 deletions packages/boot/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,6 @@ features = [
"CssStyleDeclaration",
]
version = "^0.3"

[dev-dependencies]
wasm-bindgen-test = "^0.3"
90 changes: 57 additions & 33 deletions packages/boot/tests/register_routes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ mod test {
use yew::prelude::*;
use yew_router::prelude::*;

use hikari_boot::{DeriveAppProps, DeriveAppStates, DeriveApplication, DeriveRoutes};
use hikari_boot::{
DeriveAppProps, DeriveAppStates, DeriveApplication, DeriveApplicationType, DeriveRoutes,
};

#[function_component]
fn Portal() -> yew::Html {
Expand All @@ -16,49 +18,71 @@ mod test {
}
}

// #[derive(yew::Properties, PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]
// pub struct ThreadProps {
// pub id: String,
// }

// #[function_component]
// fn Thread(props: &ThreadProps) -> yew::Html {
// html! {
// <div>{format!("Thread {}", props.id)}</div>
// }
// }

#[derive(Clone, Debug)]
pub struct Router;
#[derive(Properties, Clone, PartialEq, Debug, Serialize, Deserialize)]
pub struct ThreadProps {
pub id: String,
}

impl DeriveApplication for Router {
type Routes = Routes;
type AppProps = PageProps;
type AppStates = Theme;
#[function_component]
fn Thread(props: &ThreadProps) -> yew::Html {
html! {
<div>{format!("Thread {}", props.id)}</div>
}
}

#[derive(PartialEq, Eq, Clone, Debug, DeriveRoutes, Routable)]
#[derive(PartialEq, Clone, Debug, DeriveRoutes, Routable)]
pub enum Routes {
#[at("/")]
#[component(Portal)]
Portal,
//
// #[at("/t/:id")]
// #[component(Thread)]
// Thread { id: String },

#[at("/t/:id")]
Thread { id: String },
}

#[derive(PartialEq, Eq, Clone, Debug, DeriveAppStates, Serialize, Deserialize)]
pub struct Theme {
#[derive(PartialEq, Clone, Debug, DeriveAppStates, Serialize, Deserialize)]
pub struct AppStates {
pub color: String,
}

#[derive(PartialEq, Eq, Clone, Debug, DeriveAppProps, Serialize, Deserialize)]
pub enum PageProps {
#[derive(PartialEq, Clone, Debug, DeriveAppProps, Serialize, Deserialize)]
pub enum AppProps {
#[component(Portal)]
Portal,
//
// Thread {
// id: String,
// },

#[component(Thread)]
Thread(ThreadProps),
}

#[derive(Clone, Debug, DeriveApplication)]
pub struct App;

impl DeriveApplicationType for App {
type Routes = Routes;
type AppProps = AppProps;
type AppStates = AppStates;
}

#[test]
fn render_on_server() {
let html = App.ServerApp("/", AppProps::Portal);

assert_eq!(
html,
yew::html! {
<div>{"Portal"}</div>
}
);
}

#[wasm_bindgen_test::wasm_bindgen_test]
fn render_on_client() {
let html = App.App();

assert_eq!(
html,
yew::html! {
<div>{"Portal"}</div>
}
);
}
}
29 changes: 13 additions & 16 deletions packages/macro-types/src/register.rs
Original file line number Diff line number Diff line change
@@ -1,38 +1,35 @@
#![allow(non_snake_case)]

pub trait DeriveRoutesTrait: yew_router::Routable {
fn switch(&self) -> yew::Html;
}
pub trait Routes: yew_router::Routable {}

pub trait DeriveAppPropsTrait {
type AppProps;
}
pub trait AppProps {}

pub trait DeriveAppStatesTrait {
type AppStates;
}
pub trait AppStates {}

#[derive(Debug, PartialEq, Clone)]
pub struct AppContext<T>
where
T: DeriveAppPropsTrait,
T: AppProps,
{
pub style_manager: stylist::manager::StyleManager,
pub uri: String,
pub queries: std::collections::HashMap<String, String>,
pub page_data: T,
}

pub trait Application: DeriveApplication {
pub trait Application: DeriveApplicationType {
fn switch(&self) -> yew::Html;

fn App(&self) -> yew::Html;
fn ServerApp(&self, props: &AppContext<<Self as DeriveApplication>::AppProps>) -> yew::Html;
fn ServerApp(&self, props: &AppContext<<Self as DeriveApplicationType>::AppProps>)
-> yew::Html;
}

pub trait DeriveApplication
pub trait DeriveApplicationType
where
Self::Routes: DeriveRoutesTrait,
Self::AppProps: DeriveAppPropsTrait,
Self::AppStates: DeriveAppStatesTrait,
Self::Routes: Routes + yew_router::Routable,
Self::AppProps: AppProps,
Self::AppStates: AppStates,
{
type Routes;
type AppProps;
Expand Down
10 changes: 8 additions & 2 deletions packages/macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,19 @@ use syn::parse_macro_input;

mod utils;

#[proc_macro_derive(DeriveRoutes, attributes(component))]
#[proc_macro_derive(DeriveApplication)]
pub fn app(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as utils::app::DeriveApp);
utils::app::root(input).into()
}

#[proc_macro_derive(DeriveRoutes)]
pub fn routes(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as utils::routes::DeriveRoutes);
utils::routes::root(input).into()
}

#[proc_macro_derive(DeriveAppProps)]
#[proc_macro_derive(DeriveAppProps, attributes(component))]
pub fn app_props(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as utils::app_props::DeriveAppProps);
utils::app_props::root(input).into()
Expand Down
58 changes: 58 additions & 0 deletions packages/macro/src/utils/app.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
use proc_macro2::TokenStream;
use quote::quote;
use syn::parse::{Parse, ParseStream};
use syn::spanned::Spanned;
use syn::{Data, DeriveInput, Ident};

pub struct DeriveApp {
ident: Ident,
}

impl Parse for DeriveApp {
fn parse(input: ParseStream) -> syn::Result<Self> {
let DeriveInput { ident, data, .. } = input.parse()?;

match data {
Data::Enum(e) => {
return Err(syn::Error::new(
e.enum_token.span(),
"expected struct, found enum",
))
}
Data::Struct(data) => data,
Data::Union(u) => {
return Err(syn::Error::new(
u.union_token.span(),
"expected enum, found union",
))
}
};

Ok(Self { ident })
}
}

pub fn root(input: DeriveApp) -> TokenStream {
let DeriveApp { ident, .. } = &input;

quote! {
#[automatically_derived]
impl ::hikari_boot::Application for #ident {
#[allow(bindings_with_variant_name)]
fn switch() -> ::yew::Html {
todo!("implement switch")
}

fn App() -> yew::Html {
todo!("implement App")
}

fn ServerApp(
url: &String,
props: &::hikari_boot::AppContext<<Self as ::hikari_boot::Application>::AppProps>
) -> yew::Html {
todo!("implement ServerApp")
}
}
}
}
111 changes: 104 additions & 7 deletions packages/macro/src/utils/app_props.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,124 @@
use std::collections::HashMap;

use proc_macro2::TokenStream;
use quote::quote;
use syn::parse::{Parse, ParseStream};
use syn::{DeriveInput, Ident};
use syn::{
parse::{Parse, ParseStream},
punctuated::Punctuated,
spanned::Spanned,
Data, DeriveInput, Fields, Ident, Path, Type, Variant,
};

const COMPONENT_ATTR_IDENT: &str = "component";

#[derive(Debug, Clone, PartialEq)]
pub struct Component {
component: Path,
props: Option<Type>,
}

pub type Components = HashMap<Ident, Component>;

pub struct DeriveAppProps {
ident: Ident,
components: Components,
}

impl Parse for DeriveAppProps {
fn parse(input: ParseStream) -> syn::Result<Self> {
let DeriveInput { ident, .. } = input.parse()?;
let DeriveInput { ident, data, .. } = input.parse()?;

let data = match data {
Data::Enum(data) => data,
Data::Struct(s) => {
return Err(syn::Error::new(
s.struct_token.span(),
"expected enum, found struct",
))
}
Data::Union(u) => {
return Err(syn::Error::new(
u.union_token.span(),
"expected enum, found union",
))
}
};

let components = parse_variants_attributes(&data.variants)?;

Ok(Self { ident, components })
}
}

fn parse_variants_attributes(
variants: &Punctuated<Variant, syn::token::Comma>,
) -> syn::Result<Components> {
let mut components: Components = Default::default();

for variant in variants.iter() {
let attrs = &variant.attrs;
let at_attrs = attrs
.iter()
.filter(|attr| attr.path().is_ident(COMPONENT_ATTR_IDENT))
.collect::<Vec<_>>();

Ok(Self { ident })
let attr = match at_attrs.len() {
1 => *at_attrs.first().unwrap(),
0 => {
return Err(syn::Error::new(
variant.span(),
format!("{COMPONENT_ATTR_IDENT} attribute must be present on every variant"),
))
}
_ => {
return Err(syn::Error::new_spanned(
quote! { #(#at_attrs)* },
format!("only one {COMPONENT_ATTR_IDENT} attribute must be present"),
))
}
};

let component_path = attr.parse_args::<Path>()?;

let field = match &variant.fields {
Fields::Named(ref field) => {
return Err(syn::Error::new(
field.span(),
"only unnamed fields are supported",
));
}
Fields::Unnamed(ref fields) => {
if fields.unnamed.len() > 1 {
return Err(syn::Error::new(
fields.span(),
"only one field is supported",
));
}
Some(fields.unnamed.first().unwrap().ty.clone())
}
Fields::Unit => None,
};

components.insert(
variant.ident.clone(),
Component {
component: component_path,
props: field,
},
);
}

Ok(components)
}

pub fn root(input: DeriveAppProps) -> TokenStream {
let DeriveAppProps { ident, .. } = &input;
let DeriveAppProps {
components, ident, ..
} = &input;

quote! {
#[automatically_derived]
impl ::hikari_boot::DeriveAppPropsTrait for #ident {
type AppProps = Self;
impl ::hikari_boot::AppProps for #ident {
}
}
}
2 changes: 1 addition & 1 deletion packages/macro/src/utils/app_states.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pub fn root(input: DeriveAppStates) -> TokenStream {

quote! {
#[automatically_derived]
impl ::hikari_boot::DeriveAppStatesTrait for #ident {
impl ::hikari_boot::AppStates for #ident {
type AppStates = Self;
}
}
Expand Down
Loading

0 comments on commit 0f0f21d

Please sign in to comment.