Skip to content

Commit

Permalink
PoC: Async action support (#118)
Browse files Browse the repository at this point in the history
* poc AsyncActionPlugin

* mockup AsyncActionPlugin derive macro

* poc AsyncActionCreator and add NoAsynAction type alias

* replace UiActionParser with ActionParser

* support AsyncAction with CliPlugin

* add DipStartupStage
  • Loading branch information
JunichiSugiura authored Oct 10, 2022
1 parent d3d6c1d commit f15ec28
Show file tree
Hide file tree
Showing 39 changed files with 653 additions and 308 deletions.
20 changes: 17 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,20 @@ keywords = ["declarative-ui", "ecs", "bevy", "dioxus", "cross-platform"]
bevy = { version = "0.8", default-features = false }
clap = { version = "3.2", features = ["derive"], optional = true }
dioxus = { version = "0.2", features = ["fermi"] }
tokio = { version = "1.18", features = ["rt-multi-thread", "sync"], default-features = false }

dip_core = { version = "0.1", path = "./packages/core" }
dip_desktop = { version = "0.1", path = "./packages/desktop", optional = true }
dip_macro = { version = "0.1", path = "./packages/macro" }

dip_cli = { version = "0.1", path = "./packages/cli", optional = true }
dip_desktop = { version = "0.1", path = "./packages/desktop", optional = true }

[dev-dependencies]
leafwing-input-manager = { version = "0.5", default-features = false }
config = "0.13"
serde = { version = "1.0", features = ["derive"] }
dirs = "4.0"
reqwest = { version = "0.11", features = ["json"] }

[features]
default = ["cli"]
Expand All @@ -37,6 +40,7 @@ members = [
"packages/core",
"packages/desktop",
"packages/macro",
"packages/task",
"examples/todomvc",
]

Expand All @@ -46,22 +50,32 @@ name = "cli"
path = "examples/cli/cli.rs"
required-features = ["cli"]

[[example]]
name = "cli_async"
path = "examples/cli/async.rs"
required-features = ["cli"]

[[example]]
name = "cli_config"
path = "examples/cli/config/main.rs"
required-features = ["cli"]

# Desktop
[[example]]
name = "minimum"
path = "examples/desktop/minimum.rs"
name = "desktop_async"
path = "examples/desktop/async.rs"
required-features = ["desktop"]

[[example]]
name = "counter"
path = "examples/desktop/counter.rs"
required-features = ["desktop"]

[[example]]
name = "minimum"
path = "examples/desktop/minimum.rs"
required-features = ["desktop"]

[[example]]
name = "root_props"
path = "examples/desktop/root_props.rs"
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ fn main() {
title: "dip Plugin Example".to_string(),
..Default::default()
})
.add_plugin(DesktopPlugin::<NoUiState, NoUiAction>::new(Root))
.add_plugin(DesktopPlugin::<NoUiState, NoUiAction, NoAsyncAction>::new(Root))
.run();
}
Expand Down Expand Up @@ -80,7 +80,7 @@ fn main() {
title: "Desktop App".to_string(),
..Default::default()
})
.add_plugin(DesktopPlugin::<NoUiState, NoUiAction>::new(Root))
.add_plugin(DesktopPlugin::<NoUiState, NoUiAction, NoAsyncAction>::new(Root))
.run();
}
Expand Down Expand Up @@ -127,7 +127,7 @@ use dip::{bevy::log::LogPlugin, prelude::*};
fn main() {
App::new()
.add_plugin(CliPlugin)
.add_plugin(CliPlugin::<NoAsyncAction>::oneshot())
.add_plugin(ActionPlugin)
.add_plugin(LogPlugin)
.add_system(log_root_arg)
Expand Down Expand Up @@ -248,7 +248,7 @@ use dip::prelude::*;
fn main() {
App::new()
// Step 7. Put it all together
.add_plugin(DesktopPlugin::<UiState, UiAction>::new(Root))
.add_plugin(DesktopPlugin::<UiState, UiAction, NoAsyncAction>::new(Root))
.add_plugin(UiStatePlugin) // generated by #[ui_state]
.add_plugin(UiActionPlugin) // generated by #[ui_action]
.add_system(update_name)
Expand Down Expand Up @@ -308,7 +308,7 @@ fn Root(cx: Scope) -> Element {
// Step 5. Select state
let name = use_read(&cx, NAME);
let window = use_window::<UiAction>(&cx);
let window = use_window::<UiAction, NoAsyncAction>(&cx);
cx.render(rsx! {
h1 { "Hello, {name.value} !" }
Expand Down
114 changes: 114 additions & 0 deletions examples/cli/async.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
use dip::{
bevy::{
app::AppExit,
log::{self, LogPlugin},
},
prelude::*,
};
use serde::Deserialize;

fn main() {
App::new()
.add_plugin(CliPlugin::<AsyncAction>::continuous())
.add_plugin(ActionPlugin)
.add_plugin(AsyncActionPlugin)
.add_plugin(LogPlugin)
.add_startup_system(fetch_ip_address)
.add_startup_system(fetch_user_agent)
.add_system(handle_get_ip_address)
.add_system(handle_get_user_agent)
.run();
}

#[derive(CliPlugin, clap::Parser)]
struct Cli {
#[clap(subcommand)]
action: Action,
}

#[derive(SubcommandPlugin, clap::Subcommand, Clone)]
pub enum Action {
IpAddress,
UserAgent,
}

#[allow(dead_code)]
#[derive(Clone, Debug, Deserialize, Default)]
pub struct GetIpAddress {
origin: String,
}

#[allow(dead_code)]
#[derive(Clone, Debug, Deserialize, Default)]
pub struct GetUserAgent {
#[serde(rename = "user-agent")]
user_agent: Option<String>,
}

#[async_action]
impl AsyncActionCreator {
async fn get_ip_address() -> GetIpAddress {
reqwest::get("https://httpbin.org/ip")
.await
.unwrap()
.json::<GetIpAddress>()
.await
.unwrap()
}

async fn get_user_agent() -> GetUserAgent {
static APP_USER_AGENT: &str =
concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),);
let client = reqwest::Client::builder()
.user_agent(APP_USER_AGENT)
.build()
.unwrap();

client
.get("https://httpbin.org/user-agent")
.send()
.await
.unwrap()
.json::<GetUserAgent>()
.await
.unwrap()
}
}

fn fetch_ip_address(
mut events: EventReader<IpAddressAction>,
async_action: Res<AsyncActionPool<AsyncAction>>,
) {
for _ in events.iter() {
async_action.send(AsyncAction::get_ip_address());
}
}

fn fetch_user_agent(
mut events: EventReader<UserAgentAction>,
async_action: Res<AsyncActionPool<AsyncAction>>,
) {
for _ in events.iter() {
async_action.send(AsyncAction::get_user_agent());
}
}

fn handle_get_ip_address(
mut actions: EventReader<GetIpAddress>,
mut app_exit: EventWriter<AppExit>,
) {
for action in actions.iter() {
log::info!("{action:#?}");
app_exit.send(AppExit);
}
}

fn handle_get_user_agent(
mut actions: EventReader<GetUserAgent>,
mut app_exit: EventWriter<AppExit>,
) {
for action in actions.iter() {
log::info!("{action:#?}");
app_exit.send(AppExit);
}
}
143 changes: 1 addition & 142 deletions examples/cli/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use dip::{bevy::log::LogPlugin, prelude::*};

fn main() {
App::new()
.add_plugin(CliPlugin)
.add_plugin(CliPlugin::<NoAsyncAction>::oneshot())
.add_plugin(ActionPlugin)
.add_plugin(TodoActionPlugin)
.add_plugin(LogPlugin)
Expand Down Expand Up @@ -116,144 +116,3 @@ fn handle_list_todo(mut events: EventReader<ListTodoAction>) {
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());
// }
// }
// }
// }
Loading

1 comment on commit f15ec28

@vercel
Copy link

@vercel vercel bot commented on f15ec28 Oct 10, 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.