diff --git a/packages/liveview/Cargo.toml b/packages/liveview/Cargo.toml index cbd7b75f5..0f35f4ae6 100644 --- a/packages/liveview/Cargo.toml +++ b/packages/liveview/Cargo.toml @@ -38,6 +38,10 @@ salvo = { version = "0.44.1", optional = true, features = ["ws"] } once_cell = "1.17.1" async-trait = "0.1.71" +# rocket +rocket = { version = "0.5.0", optional = true } +rocket_ws = { version = "0.1.0", optional = true } + # actix is ... complicated? # actix-files = { version = "0.6.2", optional = true } # actix-web = { version = "4.2.1", optional = true } @@ -49,13 +53,16 @@ tokio = { workspace = true, features = ["full"] } dioxus = { workspace = true } warp = "0.3.3" axum = { version = "0.6.1", features = ["ws"] } -salvo = { version = "0.44.1", features = ["affix", "ws"] } +# salvo = { version = "0.44.1", features = ["affix", "ws"] } +rocket = "0.5.0" +rocket_ws = "0.1.0" tower = "0.4.13" [features] default = ["hot-reload"] # actix = ["actix-files", "actix-web", "actix-ws"] hot-reload = ["dioxus-hot-reload"] +rocket = ["dep:rocket", "dep:rocket_ws"] [[example]] name = "axum" @@ -68,3 +75,7 @@ required-features = ["salvo"] [[example]] name = "warp" required-features = ["warp"] + +[[example]] +name = "rocket" +required-features = ["rocket"] diff --git a/packages/liveview/README.md b/packages/liveview/README.md index cbae3c53a..13af3c36f 100644 --- a/packages/liveview/README.md +++ b/packages/liveview/README.md @@ -28,6 +28,7 @@ The current backend frameworks supported include: - Axum - Warp - Salvo +- Rocket Dioxus-LiveView exports some primitives to wire up an app into an existing backend framework. diff --git a/packages/liveview/examples/rocket.rs b/packages/liveview/examples/rocket.rs new file mode 100644 index 000000000..8256db7f8 --- /dev/null +++ b/packages/liveview/examples/rocket.rs @@ -0,0 +1,76 @@ +#[macro_use] +extern crate rocket; + +use dioxus::prelude::*; +use dioxus_liveview::LiveViewPool; +use rocket::response::content::RawHtml; +use rocket::{Config, Rocket, State}; +use rocket_ws::{Channel, WebSocket}; + +fn app(cx: Scope) -> Element { + let mut num = use_state(cx, || 0); + + cx.render(rsx! { + div { + "hello Rocket! {num}" + button { onclick: move |_| num += 1, "Increment" } + } + }) +} + +fn index_page_with_glue(glue: &str) -> RawHtml { + RawHtml(format!( + r#" + + + Dioxus LiveView with Rocket +
+ {glue} + + "#, + glue = glue + )) +} + +#[get("/")] +async fn index(config: &Config) -> RawHtml { + index_page_with_glue(&dioxus_liveview::interpreter_glue(&format!( + "ws://{addr}:{port}/ws", + addr = config.address, + port = config.port, + ))) +} + +#[get("/as-path")] +async fn as_path() -> RawHtml { + index_page_with_glue(&dioxus_liveview::interpreter_glue("/ws")) +} + +#[get("/ws")] +fn ws(ws: WebSocket, pool: &State) -> Channel<'static> { + let pool = pool.inner().to_owned(); + + ws.channel(move |stream| { + Box::pin(async move { + let _ = pool + .launch(dioxus_liveview::rocket_socket(stream), app) + .await; + Ok(()) + }) + }) +} + +#[tokio::main] +async fn main() { + let view = dioxus_liveview::LiveViewPool::new(); + + Rocket::build() + .manage(view) + .mount("/", routes![index, as_path, ws]) + .ignite() + .await + .expect("Failed to ignite rocket") + .launch() + .await + .expect("Failed to launch rocket"); +} diff --git a/packages/liveview/src/adapters/rocket_adapter.rs b/packages/liveview/src/adapters/rocket_adapter.rs new file mode 100644 index 000000000..8a9840bf2 --- /dev/null +++ b/packages/liveview/src/adapters/rocket_adapter.rs @@ -0,0 +1,25 @@ +use crate::{LiveViewError, LiveViewSocket}; +use rocket::futures::{SinkExt, StreamExt}; +use rocket_ws::{result::Error, stream::DuplexStream, Message}; + +/// Convert a rocket websocket into a LiveViewSocket +/// +/// This is required to launch a LiveView app using the rocket web framework +pub fn rocket_socket(stream: DuplexStream) -> impl LiveViewSocket { + stream + .map(transform_rx) + .with(transform_tx) + .sink_map_err(|_| LiveViewError::SendingFailed) +} + +fn transform_rx(message: Result) -> Result, LiveViewError> { + message + .map_err(|_| LiveViewError::SendingFailed)? + .into_text() + .map(|s| s.into_bytes()) + .map_err(|_| LiveViewError::SendingFailed) +} + +async fn transform_tx(message: Vec) -> Result { + Ok(Message::Text(String::from_utf8_lossy(&message).to_string())) +} diff --git a/packages/liveview/src/lib.rs b/packages/liveview/src/lib.rs index 6a2768fd5..40d6186dc 100644 --- a/packages/liveview/src/lib.rs +++ b/packages/liveview/src/lib.rs @@ -18,6 +18,11 @@ pub mod adapters { #[cfg(feature = "salvo")] pub use salvo_adapter::*; + + #[cfg(feature = "rocket")] + pub mod rocket_adapter; + #[cfg(feature = "rocket")] + pub use rocket_adapter::*; } pub use adapters::*;