From 059118c535a2135c12c2f404d41a334b3e02b340 Mon Sep 17 00:00:00 2001 From: Marc Planard Date: Thu, 29 Feb 2024 15:54:45 +0100 Subject: [PATCH] refactoring --- Cargo.toml | 1 + pub/index.html | 2 + pub/main.js | 17 ++++--- src/gen_server.rs | 13 ++--- src/main.rs | 14 +++--- src/ws_client.rs | 119 +++++++++++++++++++++++++--------------------- 6 files changed, 93 insertions(+), 73 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 80909de..03a8543 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +anyhow = "1.0.75" axum = { version = "0.6.20", features = ["ws", "headers"] } futures = "0.3.28" geo = "0.26.0" diff --git a/pub/index.html b/pub/index.html index 41ae32d..90180b6 100644 --- a/pub/index.html +++ b/pub/index.html @@ -14,6 +14,8 @@ class="button2x" value="#ffffff"> +
diff --git a/pub/main.js b/pub/main.js index 511594d..bcca9f5 100644 --- a/pub/main.js +++ b/pub/main.js @@ -55,7 +55,7 @@ const updateCanvas = (canvas, ctx, lines) => { } }; -const initDrawing = (canvas, clearButton, selectedColor, ws) => { +const initDrawing = (canvas, clearButton, errButton, selectedColor, ws) => { const ctx = canvas.getContext("2d"); ctx.lineWidth = 2; let lines = []; @@ -68,10 +68,14 @@ const initDrawing = (canvas, clearButton, selectedColor, ws) => { resetCanvas(); clearButton.onclick = evt => { - lines = []; - resetCanvas(); ws.send(JSON.stringify({ t: "clear" })); }; + + if (errButton) { + errButton.onclick = evt => { + ws.send(JSON.stringify({ t: "error", msg: "this is an error" })); + }; + } canvas.addEventListener("mousedown", evt => { const pt = getMousePoint(canvas, evt, selectedColor.value); @@ -89,7 +93,7 @@ const initDrawing = (canvas, clearButton, selectedColor, ws) => { }); canvas.addEventListener("mouseup", evt => { - console.log(currentLine); + // console.log(currentLine); currentLine = null; ws.send(JSON.stringify({ t: "stroke"})); }); @@ -109,7 +113,7 @@ const initDrawing = (canvas, clearButton, selectedColor, ws) => { }; const initWs = errorBox => { - const socket = new WebSocket('ws://localhost:3000/ws'); + const socket = new WebSocket('ws://'+ location.host + '/ws'); socket.addEventListener('open', function (event) { console.log("Open", event); @@ -131,9 +135,10 @@ window.onload = () => { const colorsDiv = document.querySelector("#colors"); const selectedColor = document.querySelector("#selectedColor"); const clearButton = document.querySelector("#clearButton"); + const errButton = document.querySelector("#errButton"); const errorBox = document.querySelector("#errorBox"); const canvas = document.querySelector("#canvas"); initUI(colorsDiv, selectedColor); let ws = initWs(errorBox); - initDrawing(canvas, clearButton, selectedColor, ws); + initDrawing(canvas, clearButton, errButton, selectedColor, ws); }; diff --git a/src/gen_server.rs b/src/gen_server.rs index 758321b..3a75e54 100644 --- a/src/gen_server.rs +++ b/src/gen_server.rs @@ -25,7 +25,10 @@ async fn gen_server(mut rx: Receiver) { match msg { GSMsg::NewClient((addr, c_tx)) => { for line in &lines { - c_tx.send(GSMsg::NewLine(line.clone())).await.unwrap(); + let ret = c_tx.send(GSMsg::NewLine(line.clone())).await; + if let Err(err) = ret { + tracing::warn!("Client {addr} send error: {err}"); + } } clients.insert(addr, c_tx); tracing::info!("NewClient {addr}"); @@ -53,11 +56,9 @@ async fn send_all( let mut to_remove : Vec = vec![]; for (addr, ref mut tx) in clients.iter() { - let ret = tx - .send(msg.clone()) - .await; - if ret.is_err() { - tracing::warn!("Client {addr} abruptly disconnected"); + let ret = tx.send(msg.clone()).await; + if let Err(err) = ret { + tracing::warn!("Client {addr} abruptly disconnected: {err}"); to_remove.push(*addr); } } diff --git a/src/main.rs b/src/main.rs index cb957cc..0d88da2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,9 +25,10 @@ use gen_server::GSMsg; const LISTEN_ON : &str = "0.0.0.0:3000"; #[tokio::main] -async fn main() { +async fn main() -> anyhow::Result<()> { tracing_subscriber::registry() - .with(tracing_subscriber::EnvFilter::try_from_default_env() + //.with(tracing_subscriber::EnvFilter::try_from_default_env() + .with(tracing_subscriber::EnvFilter::try_from_env("LJ_SKETCH") .unwrap_or_else(|_| "lj_sketch=info,tower_http=info".into())) .with(tracing_subscriber::fmt::layer()) .init(); @@ -45,13 +46,12 @@ async fn main() { .make_span_with(DefaultMakeSpan::default() .include_headers(false))); - let addr : SocketAddr = LISTEN_ON.parse().unwrap(); - - tracing::info!("listening on {}", addr); + let addr : SocketAddr = LISTEN_ON.parse()?; + tracing::info!("listening on {}", addr); axum::Server::bind(&addr) .serve(app.into_make_service_with_connect_info::()) - .await - .unwrap(); + .await?; + Ok(()) } async fn ws_handler( diff --git a/src/ws_client.rs b/src/ws_client.rs index adc32a2..49c45d7 100644 --- a/src/ws_client.rs +++ b/src/ws_client.rs @@ -1,8 +1,10 @@ -use axum::extract::ws::{Message, Message::Text, Message::Close, WebSocket}; +use axum::extract::ws::{Message, WebSocket}; use std::net::SocketAddr; use tokio::sync::mpsc::{self, Sender}; use serde::{Serialize, Deserialize}; use core::ops::ControlFlow; +use anyhow::{Result,anyhow}; + use crate::gen_server::GSMsg; use crate::line::{Line, simplify_line}; @@ -22,12 +24,23 @@ enum JMsg { } pub async fn handle_socket( - mut socket: WebSocket, + socket: WebSocket, who: SocketAddr, gs_tx: Sender ) { + match handle_socket_(socket, who, gs_tx).await { + Ok(()) => {}, + Err(err) => tracing::warn!("{who}: WS Handler error: {err}") + } +} + +pub async fn handle_socket_( + mut socket: WebSocket, + who: SocketAddr, + gs_tx: Sender +) -> Result<()> { let (chan_tx, mut chan_rx) = mpsc::channel(32); - gs_tx.send(GSMsg::NewClient((who, chan_tx))).await.unwrap(); + gs_tx.send(GSMsg::NewClient((who, chan_tx))).await?; let mut line : Line = vec![]; loop { @@ -37,18 +50,15 @@ pub async fn handle_socket( tracing::warn!("{who}: Error receiving packet: {msg:?}"); continue; }; - match process_ws_msg(&gs_tx, &who, &mut line, msg).await { - ControlFlow::Break(()) => break, + match process_ws_msg(&gs_tx, &who, &mut line, msg).await? { + ControlFlow::Break(()) => break Ok(()), ControlFlow::Continue(()) => {} } }, Some(msg) = chan_rx.recv() => { - process_gs_msg(&mut socket, &who, msg).await + process_gs_msg(&mut socket, &who, msg).await? }, - else => { - tracing::warn!("{who}: Connection lost unexpectedly."); - break; - } + else => break Err(anyhow!("{who}: Connection lost unexpectedly.")) } } } @@ -57,19 +67,20 @@ async fn process_gs_msg( socket: &mut WebSocket, who: &SocketAddr, msg: GSMsg -) { +) -> Result<()> { match msg { GSMsg::NewLine(line) => { - socket.send(Message::Text(line_to_json(&line))).await.unwrap(); + socket.send(Message::Text(line_to_json(&line)?)).await?; }, GSMsg::Clear => { - let msg = serde_json::to_string(&JMsg::Clear).unwrap(); - socket.send(Message::Text(msg)).await.unwrap(); + let msg = serde_json::to_string(&JMsg::Clear)?; + socket.send(Message::Text(msg)).await?; }, msg => { tracing::info!("{who} should not get this: {:?}", msg); } - } + } + Ok(()) } async fn process_ws_msg( @@ -77,75 +88,75 @@ async fn process_ws_msg( who: &SocketAddr, line: &mut Line, msg: Message -) -> ControlFlow<(),()> { +) -> Result> { match msg { - Text(text) => match serde_json::from_str(&text) { - Ok(json) => { - tracing::debug!("{who}: '{json:?}'"); - match handle_ws_msg(line, json) { - Ok(Some(req)) => gs_tx.send(req).await.unwrap(), - Ok(None) => {}, - Err(err) => { - tracing::warn!("{who}: message error: {err}"); + Message::Text(text) => { + tracing::debug!("{who}: sent: {text}"); + match serde_json::from_str(&text) { + Ok(json) => { + tracing::debug!("{who}: got json: {json:?}"); + match handle_ws_msg(line, json) { + Ok(Some(req)) => gs_tx.send(req).await?, + Ok(None) => {}, + Err(err) => { + tracing::warn!("{who}: message error: {err}"); + } } - } - }, - Err(err) => { - tracing::warn!("{who}: can't parse JSON: {err}"); + }, + Err(err) => tracing::warn!("{who}: can't parse JSON: {err} in: {text}") + } }, - Close(close) => { + Message::Close(close) => { tracing::info!("{who}: closing: {close:?}"); - gs_tx.send(GSMsg::DeleteClient(*who)).await.unwrap(); - return ControlFlow::Break(()); + gs_tx.send(GSMsg::DeleteClient(*who)).await?; + return Ok(ControlFlow::Break(())); }, _ => { tracing::warn!("{who}: can't handle message: {msg:?}"); } }; - ControlFlow::Continue(()) + Ok(ControlFlow::Continue(())) } fn handle_ws_msg( line: &mut Line, msg: JMsg -) -> Result, &'static str> { +) -> Result> { match msg { - JMsg::Clear => { - line.clear(); - return Ok(Some(GSMsg::Clear)); - }, + JMsg::Clear => Ok(Some(GSMsg::Clear)), JMsg::MoveTo { x, y, color } => { - *line = vec![ (x, y, parse_color(color)?) ]; + line.clear(); + line.push((x, y, parse_color(color)?)); + Ok(None) }, JMsg::LineTo { x, y, color } => { line.push( (x, y, parse_color(color)?) ); + Ok(None) }, - JMsg::Stroke => { - if line.len() > 1 { - let simple_line = simplify_line(line); - *line = vec![]; - return Ok(Some(GSMsg::NewLine(simple_line))); - } + JMsg::Stroke if line.len() > 1 => { + let simple_line = simplify_line(line); + line.clear(); + Ok(Some(GSMsg::NewLine(simple_line))) }, - JMsg::Line{..} => { - return Err("recieved a line message O_o"); - } - }; - Ok(None) + JMsg::Stroke => Err(anyhow!("can't stroke a 1 point 'line'")), + JMsg::Line{..} => Err(anyhow!("recieved a line message O_o")) + } } -fn line_to_json(line: &Line) -> String { +fn line_to_json(line: &Line) -> Result { let line = line.iter() .map(| (x, y, c) | (*x, *y, format!("#{:06x}", c))) .collect(); - serde_json::to_string(&JMsg::Line{ line }).unwrap() + let json = serde_json::to_string(&JMsg::Line{ line })?; + Ok(json) } -fn parse_color(s: String) -> Result { +fn parse_color(s: String) -> Result { if s.len() != 7 || &s[0..1] != "#" { - Err("badly formated color.") + Err(anyhow!("badly formated color")) } else { - u32::from_str_radix(&s[1..], 16).map_err(|_| "unable to parse color") + u32::from_str_radix(&s[1..], 16) + .map_err(|_| anyhow!("unable to parse color")) } }