diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index c7946ab..be0446e 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -18,6 +18,7 @@ jobs: - run: sudo apt-get install libudev-dev - name: Install prerequisites run: | + sudo apt-get update sudo apt-get install -y \ libudev-dev \ libwebkit2gtk-4.0-dev \ @@ -40,6 +41,7 @@ jobs: run: cargo install cargo-workspaces - name: Install prerequisites run: | + sudo apt-get update sudo apt-get install -y \ libudev-dev \ libwebkit2gtk-4.0-dev \ @@ -59,6 +61,7 @@ jobs: - uses: Swatinem/rust-cache@v2 - name: Install prerequisites run: | + sudo apt-get update sudo apt-get install -y \ libudev-dev \ libwebkit2gtk-4.0-dev \ @@ -80,6 +83,7 @@ jobs: run: cargo install cargo-workspaces - name: Install prerequisites run: | + sudo apt-get update sudo apt-get install -y \ libudev-dev \ libwebkit2gtk-4.0-dev \ diff --git a/README.md b/README.md index 82f9f48..c389b77 100644 --- a/README.md +++ b/README.md @@ -128,6 +128,7 @@ use dip::{bevy::log::LogPlugin, prelude::*}; fn main() { App::new() .add_plugin(CliPlugin) + .add_plugin(ActionPlugin) .add_plugin(LogPlugin) .add_system(log_root_arg) .add_system(log_path_flag) @@ -146,19 +147,22 @@ struct Cli { path: Option, #[clap(subcommand)] - command: Commands, + action: Action, } -#[derive(clap::Subcommand, Subcommand, Clone)] -enum Commands { +#[derive(SubcommandPlugin, clap::Subcommand, Clone)] +pub enum Action { + // Named variant Hello { name: Option }, - Task(TaskArgs), + // Unnamed + Hello2(Hello2Args), + // Unit Ping, } #[derive(clap::Args, Debug, Clone)] -struct TaskArgs { - name: String +pub struct Hello2Args { + name: Option, } fn log_root_arg(cli: Res) { @@ -173,19 +177,19 @@ fn log_path_flag(cli: Res) { } } -fn handle_hello(mut events: EventReader) { +fn handle_hello(mut events: EventReader) { for e in events.iter() { info!("Hello, {}!", e.name.clone().unwrap_or("world".to_string())); } } -fn handle_task(mut events: EventReader) { +fn handle_task(mut events: EventReader) { for e in events.iter() { - info!("{e:?}"); + info!("Hello, {}!", e.name.clone().unwrap_or("world".to_string())); } } -fn handle_ping(mut events: EventReader) { +fn handle_ping(mut events: EventReader) { for _ in events.iter() { info!("Pong !"); } @@ -212,9 +216,9 @@ OPTIONS: SUBCOMMANDS: hello + hello2 help Print this message or the help of the given subcommand(s) ping - task ``` diff --git a/examples/cli.rs b/examples/cli.rs index b9e6186..0bbc005 100644 --- a/examples/cli.rs +++ b/examples/cli.rs @@ -3,12 +3,17 @@ use dip::{bevy::log::LogPlugin, prelude::*}; fn main() { App::new() .add_plugin(CliPlugin) + .add_plugin(ActionPlugin) + .add_plugin(TodoActionPlugin) .add_plugin(LogPlugin) .add_system(log_root_arg) .add_system(log_path_flag) .add_system(handle_hello) - .add_system(handle_task) + .add_system(handle_hello2) .add_system(handle_ping) + .add_system(handle_add_todo) + .add_system(handle_remove_todo) + .add_system(handle_list_todo) .run(); } @@ -21,19 +26,43 @@ struct Cli { path: Option, #[clap(subcommand)] - command: Commands, + action: Action, } -#[derive(Subcommand, clap::Subcommand, Clone)] -enum Commands { - Hello { name: Option }, - Task(TaskArgs), +#[derive(SubcommandPlugin, clap::Subcommand, Clone)] +pub enum Action { + // Unit Ping, + // Named + Hello { + name: Option, + }, + // Unnamed + Hello2(Hello2Args), + + // Subsubcommand + #[clap(subcommand)] + Todo(TodoAction), +} + +#[derive(clap::Args, Clone, Debug)] +pub struct Hello2Args { + name: Option, +} + +#[derive(SubcommandPlugin, clap::Subcommand, Clone, Debug)] +pub enum TodoAction { + // Unit + List, + // Named + Add { name: Option }, + // Unnamed + Remove(RemoveTodoAction), } #[derive(clap::Args, Clone, Debug)] -struct TaskArgs { - value: Option, +pub struct RemoveTodoAction { + name: Option, } fn log_root_arg(cli: Res) { @@ -48,20 +77,183 @@ fn log_path_flag(cli: Res) { } } -fn handle_hello(mut events: EventReader) { +fn handle_hello(mut events: EventReader) { for e in events.iter() { info!("Hello, {}!", e.name.clone().unwrap_or("world".to_string())); } } -fn handle_task(mut events: EventReader) { +fn handle_hello2(mut events: EventReader) { for e in events.iter() { - info!("Task: {e:?}"); + info!("Hello, {}!", e.name.clone().unwrap_or("world".to_string())); } } -fn handle_ping(mut events: EventReader) { +fn handle_ping(mut events: EventReader) { for _ in events.iter() { info!("Pong !"); } } + +fn handle_add_todo(mut events: EventReader) { + for e in events.iter() { + info!("{e:?}"); + info!( + "AddTodoAction: {}", + e.name.clone().unwrap_or("".to_string()) + ); + } +} + +fn handle_remove_todo(mut events: EventReader) { + for e in events.iter() { + info!("{e:?}"); + } +} + +fn handle_list_todo(mut events: EventReader) { + for e in events.iter() { + info!("{e:?}"); + } +} + +// generated + +// // CliPlugin +// pub struct CliPlugin; + +// impl ::bevy::app::Plugin for CliPlugin { +// fn build(&self, app: &mut ::bevy::app::App) { +// use clap::Parser; +// // use ::dip::bevy::ecs::{ +// // schedule::ParallelSystemDescriptorCoercion, +// // system::IntoSystem, +// // }; + +// let cli = Cli::parse(); + +// app.add_plugin(::dip::core::schedule::UiSchedulePlugin) +// .insert_resource(cli.action.clone()) +// .insert_resource(cli) +// .add_event::() +// .set_runner(|mut app| { +// app.update(); +// }) +// .add_system_to_stage( +// ::dip::core::schedule::UiStage::Action, +// convert_subcommand_to_event.before(handle_action), +// ); +// } +// } + +// fn convert_subcommand_to_event( +// subcommand: ::dip::bevy::ecs::system::Res, +// mut action: ::dip::bevy::ecs::event::EventWriter, +// ) { +// action.send(subcommand.clone()); +// } + +// // Action Subcommand +// pub struct ActionPlugin; + +// impl ::bevy::app::Plugin for ActionPlugin { +// fn build(&self, app: &mut ::bevy::app::App) { +// // use ::dip::bevy::ecs::{ +// // schedule::ParallelSystemDescriptorCoercion, +// // system::IntoSystem, +// // }; + +// app.add_event::() +// .add_event::() +// .add_event::() +// .add_event::() +// .add_system_to_stage( +// ::dip::core::schedule::UiStage::Action, +// handle_action.before(handle_todo_action), +// ); +// } +// } + +// // Events +// #[derive(Clone, Debug)] +// pub struct PingAction; + +// #[derive(Clone, Debug)] +// pub struct HelloAction { +// name: Option, +// } + +// // only when type name is different (if variant_ident != first_field_ty) +// pub type Hello2Action = Hello2Args; + +// pub fn handle_action( +// mut events: ::dip::bevy::ecs::event::EventReader, +// mut ping_action: ::dip::bevy::ecs::event::EventWriter, +// mut hello_action: ::dip::bevy::ecs::event::EventWriter, +// mut hello2_action: ::dip::bevy::ecs::event::EventWriter, +// mut todo_action: ::dip::bevy::ecs::event::EventWriter, +// ) { +// for e in events.iter() { +// match e { +// Action::Ping => { +// ping_action.send(PingAction); +// } +// Action::Hello { name } => hello_action.send(HelloAction { name: name.clone() }), +// Action::Hello2(x) => { +// hello2_action.send(x.clone()); +// } +// Action::Todo(x) => { +// todo_action.send(x.clone()); +// } +// } +// } +// } + +// // TodoAction Subcommand +// pub struct TodoActionPlugin; + +// impl ::bevy::app::Plugin for TodoActionPlugin { +// fn build(&self, app: &mut ::bevy::app::App) { +// // use ::dip::bevy::ecs::{ +// // schedule::ParallelSystemDescriptorCoercion, +// // system::IntoSystem, +// // }; + +// app.add_event::() +// .add_event::() +// .add_event::() +// .add_system_to_stage(::dip::core::schedule::UiStage::Action, handle_todo_action); +// } +// } + +// // Events +// #[derive(Clone, Debug)] +// pub struct ListTodoAction; + +// #[derive(Clone, Debug)] +// pub struct AddTodoAction { +// name: Option, +// } + +// // pub type RemoveTodoAction = RemoveTodoAction; + +// pub fn handle_todo_action( +// mut events: ::dip::bevy::ecs::event::EventReader, +// mut list_todo_action: ::dip::bevy::ecs::event::EventWriter, +// mut add_todo_action: ::dip::bevy::ecs::event::EventWriter, +// mut remove_todo_action: ::dip::bevy::ecs::event::EventWriter, +// ) { +// for e in events.iter() { +// match e { +// TodoAction::List => { +// list_todo_action.send(ListTodoAction); +// } +// TodoAction::Add { name } => { +// add_todo_action.send(AddTodoAction { name: name.clone() }); +// } +// TodoAction::Remove(x) => { +// remove_todo_action.send(x.clone()); +// } +// } +// } +// } diff --git a/packages/cli/src/lib.rs b/packages/cli/src/lib.rs index 6f24f60..01f9006 100644 --- a/packages/cli/src/lib.rs +++ b/packages/cli/src/lib.rs @@ -1,5 +1,5 @@ -pub use dip_macro::{CliPlugin, Subcommand}; +pub use dip_macro::{CliPlugin, SubcommandPlugin}; pub mod prelude { - pub use dip_macro::{CliPlugin, Subcommand}; + pub use dip_macro::{CliPlugin, SubcommandPlugin}; } diff --git a/packages/core/src/schedule.rs b/packages/core/src/schedule.rs index 6aead5a..12c92c7 100644 --- a/packages/core/src/schedule.rs +++ b/packages/core/src/schedule.rs @@ -7,7 +7,7 @@ use bevy::{ /// The names of the default [`Ui`] stages. #[derive(Debug, Hash, PartialEq, Eq, Clone, StageLabel)] pub enum UiStage { - /// Place to write Ui event + /// Place to send Ui event or cli subcommand Action, /// Stage to query spawned component. Use this stage to add system requires to wait 1 /// frame delay. diff --git a/packages/macro/src/cli.rs b/packages/macro/src/cli.rs index 1bd8b42..f65dda1 100644 --- a/packages/macro/src/cli.rs +++ b/packages/macro/src/cli.rs @@ -1,9 +1,8 @@ -use convert_case::{Case, Casing}; use proc_macro::TokenStream; use proc_macro2::{TokenStream as TokenStream2, TokenTree}; use quote::quote; use std::str::FromStr; -use syn::{Fields, ItemEnum, ItemStruct, Variant}; +use syn::ItemStruct; pub struct CliParser { cli_struct: ItemStruct, @@ -14,11 +13,13 @@ impl CliParser { Self { cli_struct } } - pub fn parse(&self) -> CliTokenStreams { - let cli_name = &self.cli_struct.ident; + pub fn parse(&self) -> CliToken { + let ident = &self.cli_struct.ident; - let mut subcommand_resource = quote! {}; - let mut subcommand_plugin = quote! {}; + let mut token = CliToken { + cli_name: quote! { #ident }, + ..Default::default() + }; for f in self.cli_struct.fields.iter() { for a in f.attrs.iter() { @@ -30,10 +31,34 @@ impl CliParser { TokenTree::Ident(ident) => { if ident.to_string() == "subcommand" { let subcommand_name = f.ident.as_ref().unwrap(); - - subcommand_resource = quote! { .insert_resource(cli.#subcommand_name.clone()) }; - subcommand_plugin = - quote! { .add_plugin(SubcommandPlugin) }; + let ty = &f.ty; + + token.add_event = quote! { + .add_event::<#ty>() + }; + token.insert_subcommand_resource = quote! { + .insert_resource(cli.#subcommand_name.clone()) + }; + let subcommand_handler_name = TokenStream2::from_str( + &format!("handle_{}", subcommand_name), + ) + .unwrap(); + token.add_subcommand_handler = quote! { + .add_system_to_stage( + ::dip::core::schedule::UiStage::Action, + convert_subcommand_to_event.before( + #subcommand_handler_name + ) + ) + }; + token.subcommand_handler = quote! { + fn convert_subcommand_to_event( + subcommand: ::dip::bevy::ecs::system::Res<#ty>, + mut #subcommand_name: ::dip::bevy::ecs::event::EventWriter<#ty>, + ) { + #subcommand_name.send(subcommand.clone()); + } + }; } } _ => {} @@ -46,191 +71,54 @@ impl CliParser { } } - CliTokenStreams { - cli_name: quote! { #cli_name }, - subcommand_resource, - subcommand_plugin, - } + token } } #[derive(Default)] -pub struct CliTokenStreams { +pub struct CliToken { cli_name: TokenStream2, - subcommand_resource: TokenStream2, - subcommand_plugin: TokenStream2, + insert_subcommand_resource: TokenStream2, + add_event: TokenStream2, + add_subcommand_handler: TokenStream2, + subcommand_handler: TokenStream2, } -impl CliTokenStreams { +impl CliToken { pub fn gen(&self) -> TokenStream { let Self { cli_name, - subcommand_resource, - subcommand_plugin, + insert_subcommand_resource, + add_event, + add_subcommand_handler, + subcommand_handler, } = self; let gen = quote! { - use ::clap::Parser; - pub struct CliPlugin; impl ::bevy::app::Plugin for CliPlugin { fn build(&self, app: &mut ::bevy::app::App) { + use ::clap::Parser; + use ::dip::bevy::ecs::{ + schedule::ParallelSystemDescriptorCoercion, + system::IntoSystem, + }; + let cli = #cli_name::parse(); app.add_plugin(::dip::core::schedule::UiSchedulePlugin) - #subcommand_plugin - #subcommand_resource + #insert_subcommand_resource .insert_resource(cli) + #add_event .set_runner(|mut app| { app.update(); - }); - } - } - }; - - gen.into() - } -} - -pub struct SubcommandParser { - commands_enum: ItemEnum, -} - -impl SubcommandParser { - pub fn new(commands_enum: ItemEnum) -> Self { - Self { commands_enum } - } - - pub fn parse(&self) -> SubcommandTokenStreams { - let mut tokens = SubcommandTokenStreams::default(); - tokens.subcommand_ty_name = self.subcommand_ty_name(); - - for v in &self.commands_enum.variants { - tokens.events.push(Self::event(&v)); - tokens.add_events.push(Self::add_event(&v)); - tokens.event_readers.push(Self::event_reader(&v)); - tokens.handlers.push(Self::handler(&v)); - } - - tokens - } - - fn subcommand_ty_name(&self) -> TokenStream2 { - let ident = &self.commands_enum.ident; - quote! { #ident } - } - - fn event(v: &Variant) -> TokenStream2 { - let ident = &v.ident; - - match &v.fields { - Fields::Named(f) => { - quote! { - #[derive(Debug)] - pub struct #ident #f + }) + #add_subcommand_handler; } } - Fields::Unnamed(f) => { - let ty = &f.unnamed.first().unwrap().ty; - quote! { type #ident = #ty; } - } - Fields::Unit => { - let ident = &v.ident; - quote! { pub struct #ident; } - } - } - } - - fn add_event(v: &Variant) -> TokenStream2 { - let ident = &v.ident; - quote! { .add_event::<#ident>() } - } - - fn event_reader(v: &Variant) -> TokenStream2 { - let ident = &v.ident; - let event_name_snake = - TokenStream2::from_str(&ident.to_string().to_case(Case::Snake)).unwrap(); - - quote! { mut #event_name_snake: ::bevy::ecs::event::EventWriter<#ident>, } - } - - fn handler(v: &Variant) -> TokenStream2 { - let ident = &v.ident; - let event_name_snake = - TokenStream2::from_str(&ident.to_string().to_case(Case::Snake)).unwrap(); - - match &v.fields { - Fields::Named(fields) => { - let mut names = vec![]; - for f in &fields.named { - names.push(f.ident.clone().unwrap()); - } - - quote! { - Commands::#ident { #(#names)*, } => { - #event_name_snake.send(#ident { #(#names)*, }); - } - } - } - Fields::Unnamed(_f) => { - quote! { - Commands::#ident(x) => { - #event_name_snake.send(x.clone()); - } - } - } - Fields::Unit => { - quote! { - Commands::#ident => { - #event_name_snake.send(#ident); - } - } - } - } - } -} - -#[derive(Default)] -pub struct SubcommandTokenStreams { - subcommand_ty_name: TokenStream2, - events: Vec, - add_events: Vec, - event_readers: Vec, - handlers: Vec, -} - -impl SubcommandTokenStreams { - pub fn gen(&self) -> TokenStream { - let Self { - subcommand_ty_name, - events, - add_events, - event_readers, - handlers, - } = self; - - let gen = quote! { - #(#events)* - - pub struct SubcommandPlugin; - - impl ::bevy::app::Plugin for SubcommandPlugin { - fn build(&self, app: &mut ::bevy::app::App) { - app #(#add_events)* - .add_system_to_stage(::dip::core::schedule::UiStage::Action, handle_subcommand); - } - } - - fn handle_subcommand( - subcommand: Res<#subcommand_ty_name>, - #(#event_readers)* - ) { - match subcommand.clone() { - #(#handlers)* - } - } + #subcommand_handler }; gen.into() diff --git a/packages/macro/src/lib.rs b/packages/macro/src/lib.rs index 884945d..d44e1e0 100644 --- a/packages/macro/src/lib.rs +++ b/packages/macro/src/lib.rs @@ -1,11 +1,13 @@ extern crate proc_macro; mod cli; +mod subcommand; mod ui_action; mod ui_state; -use cli::{CliParser, SubcommandParser}; +use cli::CliParser; use proc_macro::TokenStream; +use subcommand::SubcommandParser; use syn::{parse_macro_input, ItemEnum, ItemImpl, ItemStruct}; use ui_action::UiActionParser; use ui_state::UiStateParser; @@ -31,8 +33,8 @@ pub fn cli_plugin(input: TokenStream) -> TokenStream { CliParser::new(input).parse().gen() } -#[proc_macro_derive(Subcommand)] -pub fn subcommand(tokens: TokenStream) -> TokenStream { +#[proc_macro_derive(SubcommandPlugin)] +pub fn subcommand_plugin(tokens: TokenStream) -> TokenStream { let input = parse_macro_input!(tokens as ItemEnum); SubcommandParser::new(input).parse().gen() diff --git a/packages/macro/src/subcommand.rs b/packages/macro/src/subcommand.rs new file mode 100644 index 0000000..c910894 --- /dev/null +++ b/packages/macro/src/subcommand.rs @@ -0,0 +1,250 @@ +use convert_case::{Case, Casing}; +use proc_macro::TokenStream; +use proc_macro2::{TokenStream as TokenStream2, TokenTree}; +use quote::quote; +use std::str::FromStr; +use syn::{Fields, ItemEnum, Variant}; + +pub struct SubcommandParser { + commands_enum: ItemEnum, +} + +impl SubcommandParser { + pub fn new(commands_enum: ItemEnum) -> Self { + Self { commands_enum } + } + + pub fn parse(&self) -> SubcommandToken { + let mut tokens = SubcommandToken { + handler_name: self.handler_name(), + plugin_name: self.plugin_name(), + subcommand_ty_name: self.subcommand_ty_name(), + add_system: self.add_system(), + ..Default::default() + }; + + for v in &self.commands_enum.variants { + tokens.events.push(self.event(&v)); + tokens.add_events.push(self.add_event(&v)); + tokens.event_readers.push(self.event_reader(&v)); + tokens.handlers.push(self.handler(&v)); + } + + tokens + } + + fn plugin_name(&self) -> TokenStream2 { + TokenStream2::from_str(&format!("{}Plugin", &self.commands_enum.ident.to_string())).unwrap() + } + + fn handler_name(&self) -> TokenStream2 { + let ty = &self.commands_enum.ident.to_string().to_case(Case::Snake); + TokenStream2::from_str(&format!("handle_{}", ty)).unwrap() + } + + fn add_system(&self) -> TokenStream2 { + let mut subsubcommand_handler_names = vec![]; + for v in self.commands_enum.variants.iter() { + for a in v.attrs.iter() { + for t in a.tokens.clone().into_iter() { + match t { + TokenTree::Group(g) => { + for s in g.stream() { + match s { + TokenTree::Ident(ident) => { + if ident.to_string() == "subcommand" { + if let syn::Fields::Unnamed(f) = &v.fields { + let subsubcommand_ty = &f.unnamed[0].ty; + let subsubcommand_ty_quote = + quote! { #subsubcommand_ty }; + let subsubcommand_name = &subsubcommand_ty_quote + .to_string() + .to_case(Case::Snake); + + subsubcommand_handler_names + .push(format!("handle_{}", subsubcommand_name)); + } + } + } + _ => {} + } + } + } + _ => {} + } + } + } + } + + let mut handler = self.handler_name().to_string(); + for n in subsubcommand_handler_names { + handler = format!("{}.before({})", handler, n); + } + + let handler_token = TokenStream2::from_str(&handler).unwrap(); + + quote! { + .add_system_to_stage(::dip::core::schedule::UiStage::Action, #handler_token); + } + } + + fn subcommand_ty_name(&self) -> TokenStream2 { + let ident = &self.commands_enum.ident; + quote! { #ident } + } + + fn event(&self, v: &Variant) -> TokenStream2 { + let name = self.event_name(v); + + match &v.fields { + Fields::Named(f) => { + quote! { + #[derive(Clone, Debug)] + pub struct #name #f + } + } + Fields::Unnamed(f) => { + let first_field_ty = &f.unnamed.first().unwrap().ty; + + // add event name alias only when type name is different (if variant_ident != first_field_ty) + if &name.to_string() == quote! { #first_field_ty }.to_string().as_str() { + quote! {} + } else { + quote! { pub type #name = #first_field_ty; } + } + } + Fields::Unit => { + quote! { + #[derive(Clone, Debug)] + pub struct #name; + } + } + } + } + + fn add_event(&self, v: &Variant) -> TokenStream2 { + let name = self.event_name(v); + + quote! { .add_event::<#name>() } + } + + fn event_name(&self, v: &Variant) -> TokenStream2 { + TokenStream2::from_str(&format!( + "{}{}", + &v.ident.to_string(), + &self.subcommand_ty_name().to_string(), + )) + .unwrap() + } + + fn event_reader(&self, v: &Variant) -> TokenStream2 { + let name = self.event_name(v); + let event_name_snake = + TokenStream2::from_str(&name.to_string().to_case(Case::Snake)).unwrap(); + + quote! { mut #event_name_snake: ::dip::bevy::ecs::event::EventWriter<#name>, } + } + + fn handler(&self, v: &Variant) -> TokenStream2 { + let subcommand_ty_name = self.subcommand_ty_name(); + let event_name = self.event_name(v); + let ident = &v.ident; + let name = self.event_name(v); + let event_name_snake = + TokenStream2::from_str(&name.to_string().to_case(Case::Snake)).unwrap(); + + match &v.fields { + Fields::Named(fields) => { + let mut field_names = vec![]; + let mut new_fields = vec![]; + for f in &fields.named { + let field_name = f.ident.clone().unwrap(); + let new_field = + TokenStream2::from_str(&format!("{field_name}: {field_name}.clone()")) + .unwrap(); + + field_names.push(field_name); + new_fields.push(new_field) + } + + quote! { + #subcommand_ty_name::#ident { #(#field_names)*, } => { + #event_name_snake.send(#event_name { #(#new_fields)*, }); + } + } + } + Fields::Unnamed(_f) => { + quote! { + #subcommand_ty_name::#ident(x) => { + #event_name_snake.send(x.clone()); + } + } + } + Fields::Unit => { + quote! { + #subcommand_ty_name::#ident => { + #event_name_snake.send(#event_name); + } + } + } + } + } +} + +#[derive(Default)] +pub struct SubcommandToken { + handler_name: TokenStream2, + plugin_name: TokenStream2, + subcommand_ty_name: TokenStream2, + events: Vec, + add_events: Vec, + event_readers: Vec, + handlers: Vec, + add_system: TokenStream2, +} + +impl SubcommandToken { + pub fn gen(&self) -> TokenStream { + let Self { + handler_name, + plugin_name, + subcommand_ty_name, + events, + add_events, + event_readers, + handlers, + add_system, + } = self; + + let gen = quote! { + pub struct #plugin_name; + + impl ::bevy::app::Plugin for #plugin_name { + fn build(&self, app: &mut ::bevy::app::App) { + use ::dip::bevy::ecs::{ + schedule::ParallelSystemDescriptorCoercion, + system::IntoSystem, + }; + + app #(#add_events)* + #add_system + } + } + + #(#events)* + + pub fn #handler_name( + mut events: ::dip::bevy::ecs::event::EventReader<#subcommand_ty_name>, + #(#event_readers)* + ) { + for e in events.iter() { + match e { + #(#handlers)* + } + } + } + }; + + gen.into() + } +} diff --git a/packages/macro/src/ui_action.rs b/packages/macro/src/ui_action.rs index 07dfbac..25d1587 100644 --- a/packages/macro/src/ui_action.rs +++ b/packages/macro/src/ui_action.rs @@ -18,8 +18,8 @@ impl From for UiActionParser { } impl UiActionParser { - pub fn parse(&self) -> UiActionTokenStreams { - let mut tokens = UiActionTokenStreams::default(); + pub fn parse(&self) -> UiActionToken { + let mut tokens = UiActionToken::default(); tokens.action_creator_name = self.action_creator_name(); tokens.action_creator_impl = self.action_creator_impl(); @@ -164,7 +164,7 @@ impl UiActionParser { } #[derive(Default)] -pub struct UiActionTokenStreams { +pub struct UiActionToken { enum_variants: Vec, add_events: Vec, handler_args: Vec, @@ -174,7 +174,7 @@ pub struct UiActionTokenStreams { methods: Vec, } -impl UiActionTokenStreams { +impl UiActionToken { pub fn gen(&self) -> TokenStream { let Self { enum_variants, diff --git a/packages/macro/src/ui_state.rs b/packages/macro/src/ui_state.rs index 14ec4a4..b665a0e 100644 --- a/packages/macro/src/ui_state.rs +++ b/packages/macro/src/ui_state.rs @@ -16,8 +16,8 @@ impl From for UiStateParser { } impl UiStateParser { - pub fn parse(&self) -> UiStateTokenStreams { - let mut tokens = UiStateTokenStreams::default(); + pub fn parse(&self) -> UiStateToken { + let mut tokens = UiStateToken::default(); for f in &self.input.fields { let name_str = f.ident.as_ref().unwrap().to_string(); @@ -165,7 +165,7 @@ impl UiStateParser { } #[derive(Default)] -pub struct UiStateTokenStreams { +pub struct UiStateToken { atom_quotes: Vec, enum_variants: Vec, variant_handlers: Vec, @@ -174,7 +174,7 @@ pub struct UiStateTokenStreams { dispatch_systems: Vec, } -impl UiStateTokenStreams { +impl UiStateToken { pub fn gen(&self) -> TokenStream { let Self { atom_quotes, diff --git a/src/main.rs b/src/main.rs index 3608022..d6312e8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,74 +1,53 @@ +mod tool; + +use crate::tool::*; use dip::{ bevy::{ app::App, - ecs::{event::EventReader, system::Res}, + ecs::event::EventReader, log::{self, LogPlugin}, }, - cli::{CliPlugin, Subcommand}, + cli::{CliPlugin, SubcommandPlugin}, }; fn main() { App::new() .add_plugin(CliPlugin) + .add_plugin(ActionPlugin) + .add_plugin(ToolActionPlugin) + .add_plugin(ConfigActionPlugin) .add_plugin(LogPlugin) - .add_system(log_root_arg) - .add_system(log_path_flag) - .add_system(handle_hello) - .add_system(handle_task) - .add_system(handle_ping) + .add_system(handle_tool_install) + .add_system(handle_config_add) .run(); } #[derive(CliPlugin, clap::Parser)] -#[clap(author, version, about, long_about = None)] -struct DipCli { - root_arg: Option, - +#[clap(version)] +struct Cli { #[clap(short, long)] path: Option, #[clap(subcommand)] - command: Commands, -} - -#[derive(Subcommand, clap::Subcommand, Clone)] -enum Commands { - Hello { name: Option }, - Task(TaskArgs), - Ping, -} - -#[derive(clap::Args, Clone, Debug)] -struct TaskArgs { - value: Option, -} - -fn log_root_arg(cli: Res) { - if let Some(arg) = &cli.root_arg { - log::info!("root arg: {:?}", arg); - } + action: Action, } -fn log_path_flag(cli: Res) { - if let Some(path) = &cli.path { - log::info!("path flag: {:?}", path); - } -} +#[derive(SubcommandPlugin, clap::Subcommand, Clone, Debug)] +pub enum Action { + #[clap(subcommand)] + Tool(ToolAction), -fn handle_hello(mut events: EventReader) { - for e in events.iter() { - log::info!("Hello, {}!", e.name.clone().unwrap_or("world".to_string())); - } + #[clap(subcommand)] + Config(ConfigAction), } -fn handle_task(mut events: EventReader) { +fn handle_config_add(mut events: EventReader) { for e in events.iter() { - log::info!("Task: {e:?}"); + log::info!("{e:#?}"); } } -fn handle_ping(mut events: EventReader) { - for _ in events.iter() { - log::info!("Pong !"); - } +#[derive(SubcommandPlugin, clap::Subcommand, Clone, Debug)] +pub enum ConfigAction { + Add, } diff --git a/src/tool.rs b/src/tool.rs new file mode 100644 index 0000000..c6df707 --- /dev/null +++ b/src/tool.rs @@ -0,0 +1,21 @@ +use dip::{ + bevy::{ecs::event::EventReader, log}, + cli::SubcommandPlugin, +}; + +#[derive(SubcommandPlugin, clap::Subcommand, Clone, Debug)] +pub enum ToolAction { + Install, +} + +impl Default for ToolAction { + fn default() -> Self { + Self::Install + } +} + +pub fn handle_tool_install(mut events: EventReader) { + for e in events.iter() { + log::info!("{e:#?}"); + } +}