Skip to content

Commit

Permalink
Support nested subcommand (#112)
Browse files Browse the repository at this point in the history
* support nested subcommand

* demonstrate complete usage with generated code in cli example

* handle edge cases

* apt-get update before installing prerequisites
  • Loading branch information
JunichiSugiura authored Sep 19, 2022
1 parent 7f983f0 commit 518bb4c
Show file tree
Hide file tree
Showing 12 changed files with 591 additions and 251 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand All @@ -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 \
Expand All @@ -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 \
Expand All @@ -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 \
Expand Down
26 changes: 15 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -146,19 +147,22 @@ struct Cli {
path: Option<String>,
#[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<String> },
Task(TaskArgs),
// Unnamed
Hello2(Hello2Args),
// Unit
Ping,
}
#[derive(clap::Args, Debug, Clone)]
struct TaskArgs {
name: String
pub struct Hello2Args {
name: Option<String>,
}
fn log_root_arg(cli: Res<Cli>) {
Expand All @@ -173,19 +177,19 @@ fn log_path_flag(cli: Res<Cli>) {
}
}
fn handle_hello(mut events: EventReader<Hello>) {
fn handle_hello(mut events: EventReader<HelloAction>) {
for e in events.iter() {
info!("Hello, {}!", e.name.clone().unwrap_or("world".to_string()));
}
}
fn handle_task(mut events: EventReader<Task>) {
fn handle_task(mut events: EventReader<Hello2Action>) {
for e in events.iter() {
info!("{e:?}");
info!("Hello, {}!", e.name.clone().unwrap_or("world".to_string()));
}
}
fn handle_ping(mut events: EventReader<Ping>) {
fn handle_ping(mut events: EventReader<PingAction>) {
for _ in events.iter() {
info!("Pong !");
}
Expand All @@ -212,9 +216,9 @@ OPTIONS:

SUBCOMMANDS:
hello
hello2
help Print this message or the help of the given subcommand(s)
ping
task

```
</details>
Expand Down
216 changes: 204 additions & 12 deletions examples/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand All @@ -21,19 +26,43 @@ struct Cli {
path: Option<String>,

#[clap(subcommand)]
command: Commands,
action: Action,
}

#[derive(Subcommand, clap::Subcommand, Clone)]
enum Commands {
Hello { name: Option<String> },
Task(TaskArgs),
#[derive(SubcommandPlugin, clap::Subcommand, Clone)]
pub enum Action {
// Unit
Ping,
// Named
Hello {
name: Option<String>,
},
// Unnamed
Hello2(Hello2Args),

// Subsubcommand
#[clap(subcommand)]
Todo(TodoAction),
}

#[derive(clap::Args, Clone, Debug)]
pub struct Hello2Args {
name: Option<String>,
}

#[derive(SubcommandPlugin, clap::Subcommand, Clone, Debug)]
pub enum TodoAction {
// Unit
List,
// Named
Add { name: Option<String> },
// Unnamed
Remove(RemoveTodoAction),
}

#[derive(clap::Args, Clone, Debug)]
struct TaskArgs {
value: Option<String>,
pub struct RemoveTodoAction {
name: Option<String>,
}

fn log_root_arg(cli: Res<Cli>) {
Expand All @@ -48,20 +77,183 @@ fn log_path_flag(cli: Res<Cli>) {
}
}

fn handle_hello(mut events: EventReader<Hello>) {
fn handle_hello(mut events: EventReader<HelloAction>) {
for e in events.iter() {
info!("Hello, {}!", e.name.clone().unwrap_or("world".to_string()));
}
}

fn handle_task(mut events: EventReader<Task>) {
fn handle_hello2(mut events: EventReader<Hello2Action>) {
for e in events.iter() {
info!("Task: {e:?}");
info!("Hello, {}!", e.name.clone().unwrap_or("world".to_string()));
}
}

fn handle_ping(mut events: EventReader<Ping>) {
fn handle_ping(mut events: EventReader<PingAction>) {
for _ in events.iter() {
info!("Pong !");
}
}

fn handle_add_todo(mut events: EventReader<AddTodoAction>) {
for e in events.iter() {
info!("{e:?}");
info!(
"AddTodoAction: {}",
e.name.clone().unwrap_or("<no-name>".to_string())
);
}
}

fn handle_remove_todo(mut events: EventReader<RemoveTodoAction>) {
for e in events.iter() {
info!("{e:?}");
}
}

fn handle_list_todo(mut events: EventReader<ListTodoAction>) {
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::<Action>()
// .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<Action>,
// mut action: ::dip::bevy::ecs::event::EventWriter<Action>,
// ) {
// 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::<PingAction>()
// .add_event::<HelloAction>()
// .add_event::<Hello2Action>()
// .add_event::<TodoAction>()
// .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<String>,
// }

// // 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<Action>,
// mut ping_action: ::dip::bevy::ecs::event::EventWriter<PingAction>,
// mut hello_action: ::dip::bevy::ecs::event::EventWriter<HelloAction>,
// mut hello2_action: ::dip::bevy::ecs::event::EventWriter<Hello2Action>,
// mut todo_action: ::dip::bevy::ecs::event::EventWriter<TodoAction>,
// ) {
// 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::<ListTodoAction>()
// .add_event::<AddTodoAction>()
// .add_event::<RemoveTodoAction>()
// .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<String>,
// }

// // pub type RemoveTodoAction = RemoveTodoAction;

// pub fn handle_todo_action(
// mut events: ::dip::bevy::ecs::event::EventReader<TodoAction>,
// mut list_todo_action: ::dip::bevy::ecs::event::EventWriter<ListTodoAction>,
// mut add_todo_action: ::dip::bevy::ecs::event::EventWriter<AddTodoAction>,
// mut remove_todo_action: ::dip::bevy::ecs::event::EventWriter<RemoveTodoAction>,
// ) {
// 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());
// }
// }
// }
// }
4 changes: 2 additions & 2 deletions packages/cli/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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};
}
2 changes: 1 addition & 1 deletion packages/core/src/schedule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Loading

1 comment on commit 518bb4c

@vercel
Copy link

@vercel vercel bot commented on 518bb4c Sep 19, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.