axum_adapter.rs 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. use std::sync::Arc;
  2. use crate::{interpreter_glue, LiveViewError, LiveViewSocket, LiveviewRouter};
  3. use axum::{
  4. extract::{
  5. ws::{Message, WebSocket},
  6. WebSocketUpgrade,
  7. },
  8. response::Html,
  9. routing::*,
  10. Router,
  11. };
  12. use futures_util::{SinkExt, StreamExt};
  13. /// Convert an Axum WebSocket into a `LiveViewSocket`.
  14. ///
  15. /// This is required to launch a LiveView app using the Axum web framework.
  16. pub fn axum_socket(ws: WebSocket) -> impl LiveViewSocket {
  17. ws.map(transform_rx)
  18. .with(transform_tx)
  19. .sink_map_err(|_| LiveViewError::SendingFailed)
  20. }
  21. fn transform_rx(message: Result<Message, axum::Error>) -> Result<Vec<u8>, LiveViewError> {
  22. message
  23. .map_err(|_| LiveViewError::SendingFailed)?
  24. .into_text()
  25. .map(|s| s.into_bytes())
  26. .map_err(|_| LiveViewError::SendingFailed)
  27. }
  28. async fn transform_tx(message: Vec<u8>) -> Result<Message, axum::Error> {
  29. Ok(Message::Binary(message))
  30. }
  31. impl LiveviewRouter for Router {
  32. fn create_default_liveview_router() -> Self {
  33. Router::new()
  34. }
  35. fn with_virtual_dom(
  36. self,
  37. route: &str,
  38. app: impl Fn() -> dioxus_core::prelude::VirtualDom + Send + Sync + 'static,
  39. ) -> Self {
  40. let view = crate::LiveViewPool::new();
  41. let ws_path = format!("{}/ws", route);
  42. let title = crate::app_title();
  43. let index_page_with_glue = move |glue: &str| {
  44. Html(format!(
  45. r#"
  46. <!DOCTYPE html>
  47. <html>
  48. <head> <title>{title}</title> </head>
  49. <body> <div id="main"></div> </body>
  50. {glue}
  51. </html>
  52. "#,
  53. ))
  54. };
  55. let app = Arc::new(app);
  56. self.route(
  57. &ws_path,
  58. get(move |ws: WebSocketUpgrade| async move {
  59. let app = app.clone();
  60. ws.on_upgrade(move |socket| async move {
  61. _ = view
  62. .launch_virtualdom(axum_socket(socket), move || app())
  63. .await;
  64. })
  65. }),
  66. )
  67. .route(
  68. route,
  69. get(move || async move { index_page_with_glue(&interpreter_glue(&ws_path)) }),
  70. )
  71. }
  72. async fn start(self, address: impl Into<std::net::SocketAddr>) {
  73. let listener = tokio::net::TcpListener::bind(address.into()).await.unwrap();
  74. if let Err(err) = axum::serve(listener, self.into_make_service()).await {
  75. eprintln!("Failed to start axum server: {}", err);
  76. }
  77. }
  78. }