refactoring
This commit is contained in:
parent
837338cb69
commit
059118c535
@ -6,6 +6,7 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
anyhow = "1.0.75"
|
||||||
axum = { version = "0.6.20", features = ["ws", "headers"] }
|
axum = { version = "0.6.20", features = ["ws", "headers"] }
|
||||||
futures = "0.3.28"
|
futures = "0.3.28"
|
||||||
geo = "0.26.0"
|
geo = "0.26.0"
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
class="button2x" value="#ffffff"></input>
|
class="button2x" value="#ffffff"></input>
|
||||||
<input type="button" id="clearButton"
|
<input type="button" id="clearButton"
|
||||||
class="button2x" value="♻"></input>
|
class="button2x" value="♻"></input>
|
||||||
|
<!-- <input type="button" id="errButton"
|
||||||
|
class="button2x" value="err"></input> !-->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
17
pub/main.js
17
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");
|
const ctx = canvas.getContext("2d");
|
||||||
ctx.lineWidth = 2;
|
ctx.lineWidth = 2;
|
||||||
let lines = [];
|
let lines = [];
|
||||||
@ -68,10 +68,14 @@ const initDrawing = (canvas, clearButton, selectedColor, ws) => {
|
|||||||
resetCanvas();
|
resetCanvas();
|
||||||
|
|
||||||
clearButton.onclick = evt => {
|
clearButton.onclick = evt => {
|
||||||
lines = [];
|
|
||||||
resetCanvas();
|
|
||||||
ws.send(JSON.stringify({ t: "clear" }));
|
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 => {
|
canvas.addEventListener("mousedown", evt => {
|
||||||
const pt = getMousePoint(canvas, evt, selectedColor.value);
|
const pt = getMousePoint(canvas, evt, selectedColor.value);
|
||||||
@ -89,7 +93,7 @@ const initDrawing = (canvas, clearButton, selectedColor, ws) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
canvas.addEventListener("mouseup", evt => {
|
canvas.addEventListener("mouseup", evt => {
|
||||||
console.log(currentLine);
|
// console.log(currentLine);
|
||||||
currentLine = null;
|
currentLine = null;
|
||||||
ws.send(JSON.stringify({ t: "stroke"}));
|
ws.send(JSON.stringify({ t: "stroke"}));
|
||||||
});
|
});
|
||||||
@ -109,7 +113,7 @@ const initDrawing = (canvas, clearButton, selectedColor, ws) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const initWs = errorBox => {
|
const initWs = errorBox => {
|
||||||
const socket = new WebSocket('ws://localhost:3000/ws');
|
const socket = new WebSocket('ws://'+ location.host + '/ws');
|
||||||
|
|
||||||
socket.addEventListener('open', function (event) {
|
socket.addEventListener('open', function (event) {
|
||||||
console.log("Open", event);
|
console.log("Open", event);
|
||||||
@ -131,9 +135,10 @@ window.onload = () => {
|
|||||||
const colorsDiv = document.querySelector("#colors");
|
const colorsDiv = document.querySelector("#colors");
|
||||||
const selectedColor = document.querySelector("#selectedColor");
|
const selectedColor = document.querySelector("#selectedColor");
|
||||||
const clearButton = document.querySelector("#clearButton");
|
const clearButton = document.querySelector("#clearButton");
|
||||||
|
const errButton = document.querySelector("#errButton");
|
||||||
const errorBox = document.querySelector("#errorBox");
|
const errorBox = document.querySelector("#errorBox");
|
||||||
const canvas = document.querySelector("#canvas");
|
const canvas = document.querySelector("#canvas");
|
||||||
initUI(colorsDiv, selectedColor);
|
initUI(colorsDiv, selectedColor);
|
||||||
let ws = initWs(errorBox);
|
let ws = initWs(errorBox);
|
||||||
initDrawing(canvas, clearButton, selectedColor, ws);
|
initDrawing(canvas, clearButton, errButton, selectedColor, ws);
|
||||||
};
|
};
|
||||||
|
@ -25,7 +25,10 @@ async fn gen_server(mut rx: Receiver<GSMsg>) {
|
|||||||
match msg {
|
match msg {
|
||||||
GSMsg::NewClient((addr, c_tx)) => {
|
GSMsg::NewClient((addr, c_tx)) => {
|
||||||
for line in &lines {
|
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);
|
clients.insert(addr, c_tx);
|
||||||
tracing::info!("NewClient {addr}");
|
tracing::info!("NewClient {addr}");
|
||||||
@ -53,11 +56,9 @@ async fn send_all(
|
|||||||
let mut to_remove : Vec<SocketAddr> = vec![];
|
let mut to_remove : Vec<SocketAddr> = vec![];
|
||||||
|
|
||||||
for (addr, ref mut tx) in clients.iter() {
|
for (addr, ref mut tx) in clients.iter() {
|
||||||
let ret = tx
|
let ret = tx.send(msg.clone()).await;
|
||||||
.send(msg.clone())
|
if let Err(err) = ret {
|
||||||
.await;
|
tracing::warn!("Client {addr} abruptly disconnected: {err}");
|
||||||
if ret.is_err() {
|
|
||||||
tracing::warn!("Client {addr} abruptly disconnected");
|
|
||||||
to_remove.push(*addr);
|
to_remove.push(*addr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
14
src/main.rs
14
src/main.rs
@ -25,9 +25,10 @@ use gen_server::GSMsg;
|
|||||||
const LISTEN_ON : &str = "0.0.0.0:3000";
|
const LISTEN_ON : &str = "0.0.0.0:3000";
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() -> anyhow::Result<()> {
|
||||||
tracing_subscriber::registry()
|
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()))
|
.unwrap_or_else(|_| "lj_sketch=info,tower_http=info".into()))
|
||||||
.with(tracing_subscriber::fmt::layer())
|
.with(tracing_subscriber::fmt::layer())
|
||||||
.init();
|
.init();
|
||||||
@ -45,13 +46,12 @@ async fn main() {
|
|||||||
.make_span_with(DefaultMakeSpan::default()
|
.make_span_with(DefaultMakeSpan::default()
|
||||||
.include_headers(false)));
|
.include_headers(false)));
|
||||||
|
|
||||||
let addr : SocketAddr = LISTEN_ON.parse().unwrap();
|
let addr : SocketAddr = LISTEN_ON.parse()?;
|
||||||
|
tracing::info!("listening on {}", addr);
|
||||||
tracing::info!("listening on {}", addr);
|
|
||||||
axum::Server::bind(&addr)
|
axum::Server::bind(&addr)
|
||||||
.serve(app.into_make_service_with_connect_info::<SocketAddr>())
|
.serve(app.into_make_service_with_connect_info::<SocketAddr>())
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn ws_handler(
|
async fn ws_handler(
|
||||||
|
119
src/ws_client.rs
119
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 std::net::SocketAddr;
|
||||||
use tokio::sync::mpsc::{self, Sender};
|
use tokio::sync::mpsc::{self, Sender};
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
use core::ops::ControlFlow;
|
use core::ops::ControlFlow;
|
||||||
|
use anyhow::{Result,anyhow};
|
||||||
|
|
||||||
use crate::gen_server::GSMsg;
|
use crate::gen_server::GSMsg;
|
||||||
use crate::line::{Line, simplify_line};
|
use crate::line::{Line, simplify_line};
|
||||||
|
|
||||||
@ -22,12 +24,23 @@ enum JMsg {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn handle_socket(
|
pub async fn handle_socket(
|
||||||
mut socket: WebSocket,
|
socket: WebSocket,
|
||||||
who: SocketAddr,
|
who: SocketAddr,
|
||||||
gs_tx: Sender<GSMsg>
|
gs_tx: Sender<GSMsg>
|
||||||
) {
|
) {
|
||||||
|
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<GSMsg>
|
||||||
|
) -> Result<()> {
|
||||||
let (chan_tx, mut chan_rx) = mpsc::channel(32);
|
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![];
|
let mut line : Line = vec![];
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
@ -37,18 +50,15 @@ pub async fn handle_socket(
|
|||||||
tracing::warn!("{who}: Error receiving packet: {msg:?}");
|
tracing::warn!("{who}: Error receiving packet: {msg:?}");
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
match process_ws_msg(&gs_tx, &who, &mut line, msg).await {
|
match process_ws_msg(&gs_tx, &who, &mut line, msg).await? {
|
||||||
ControlFlow::Break(()) => break,
|
ControlFlow::Break(()) => break Ok(()),
|
||||||
ControlFlow::Continue(()) => {}
|
ControlFlow::Continue(()) => {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Some(msg) = chan_rx.recv() => {
|
Some(msg) = chan_rx.recv() => {
|
||||||
process_gs_msg(&mut socket, &who, msg).await
|
process_gs_msg(&mut socket, &who, msg).await?
|
||||||
},
|
},
|
||||||
else => {
|
else => break Err(anyhow!("{who}: Connection lost unexpectedly."))
|
||||||
tracing::warn!("{who}: Connection lost unexpectedly.");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -57,19 +67,20 @@ async fn process_gs_msg(
|
|||||||
socket: &mut WebSocket,
|
socket: &mut WebSocket,
|
||||||
who: &SocketAddr,
|
who: &SocketAddr,
|
||||||
msg: GSMsg
|
msg: GSMsg
|
||||||
) {
|
) -> Result<()> {
|
||||||
match msg {
|
match msg {
|
||||||
GSMsg::NewLine(line) => {
|
GSMsg::NewLine(line) => {
|
||||||
socket.send(Message::Text(line_to_json(&line))).await.unwrap();
|
socket.send(Message::Text(line_to_json(&line)?)).await?;
|
||||||
},
|
},
|
||||||
GSMsg::Clear => {
|
GSMsg::Clear => {
|
||||||
let msg = serde_json::to_string(&JMsg::Clear).unwrap();
|
let msg = serde_json::to_string(&JMsg::Clear)?;
|
||||||
socket.send(Message::Text(msg)).await.unwrap();
|
socket.send(Message::Text(msg)).await?;
|
||||||
},
|
},
|
||||||
msg => {
|
msg => {
|
||||||
tracing::info!("{who} should not get this: {:?}", msg);
|
tracing::info!("{who} should not get this: {:?}", msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn process_ws_msg(
|
async fn process_ws_msg(
|
||||||
@ -77,75 +88,75 @@ async fn process_ws_msg(
|
|||||||
who: &SocketAddr,
|
who: &SocketAddr,
|
||||||
line: &mut Line,
|
line: &mut Line,
|
||||||
msg: Message
|
msg: Message
|
||||||
) -> ControlFlow<(),()> {
|
) -> Result<ControlFlow<(),()>> {
|
||||||
match msg {
|
match msg {
|
||||||
Text(text) => match serde_json::from_str(&text) {
|
Message::Text(text) => {
|
||||||
Ok(json) => {
|
tracing::debug!("{who}: sent: {text}");
|
||||||
tracing::debug!("{who}: '{json:?}'");
|
match serde_json::from_str(&text) {
|
||||||
match handle_ws_msg(line, json) {
|
Ok(json) => {
|
||||||
Ok(Some(req)) => gs_tx.send(req).await.unwrap(),
|
tracing::debug!("{who}: got json: {json:?}");
|
||||||
Ok(None) => {},
|
match handle_ws_msg(line, json) {
|
||||||
Err(err) => {
|
Ok(Some(req)) => gs_tx.send(req).await?,
|
||||||
tracing::warn!("{who}: message error: {err}");
|
Ok(None) => {},
|
||||||
|
Err(err) => {
|
||||||
|
tracing::warn!("{who}: message error: {err}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
Err(err) => tracing::warn!("{who}: can't parse JSON: {err} in: {text}")
|
||||||
Err(err) => {
|
|
||||||
tracing::warn!("{who}: can't parse JSON: {err}");
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Close(close) => {
|
Message::Close(close) => {
|
||||||
tracing::info!("{who}: closing: {close:?}");
|
tracing::info!("{who}: closing: {close:?}");
|
||||||
gs_tx.send(GSMsg::DeleteClient(*who)).await.unwrap();
|
gs_tx.send(GSMsg::DeleteClient(*who)).await?;
|
||||||
return ControlFlow::Break(());
|
return Ok(ControlFlow::Break(()));
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
tracing::warn!("{who}: can't handle message: {msg:?}");
|
tracing::warn!("{who}: can't handle message: {msg:?}");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ControlFlow::Continue(())
|
Ok(ControlFlow::Continue(()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_ws_msg(
|
fn handle_ws_msg(
|
||||||
line: &mut Line,
|
line: &mut Line,
|
||||||
msg: JMsg
|
msg: JMsg
|
||||||
) -> Result<Option<GSMsg>, &'static str> {
|
) -> Result<Option<GSMsg>> {
|
||||||
match msg {
|
match msg {
|
||||||
JMsg::Clear => {
|
JMsg::Clear => Ok(Some(GSMsg::Clear)),
|
||||||
line.clear();
|
|
||||||
return Ok(Some(GSMsg::Clear));
|
|
||||||
},
|
|
||||||
JMsg::MoveTo { x, y, color } => {
|
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 } => {
|
JMsg::LineTo { x, y, color } => {
|
||||||
line.push( (x, y, parse_color(color)?) );
|
line.push( (x, y, parse_color(color)?) );
|
||||||
|
Ok(None)
|
||||||
},
|
},
|
||||||
JMsg::Stroke => {
|
JMsg::Stroke if line.len() > 1 => {
|
||||||
if line.len() > 1 {
|
let simple_line = simplify_line(line);
|
||||||
let simple_line = simplify_line(line);
|
line.clear();
|
||||||
*line = vec![];
|
Ok(Some(GSMsg::NewLine(simple_line)))
|
||||||
return Ok(Some(GSMsg::NewLine(simple_line)));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
JMsg::Line{..} => {
|
JMsg::Stroke => Err(anyhow!("can't stroke a 1 point 'line'")),
|
||||||
return Err("recieved a line message O_o");
|
JMsg::Line{..} => Err(anyhow!("recieved a line message O_o"))
|
||||||
}
|
}
|
||||||
};
|
|
||||||
Ok(None)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn line_to_json(line: &Line) -> String {
|
fn line_to_json(line: &Line) -> Result<String> {
|
||||||
let line = line.iter()
|
let line = line.iter()
|
||||||
.map(| (x, y, c) | (*x, *y, format!("#{:06x}", c)))
|
.map(| (x, y, c) | (*x, *y, format!("#{:06x}", c)))
|
||||||
.collect();
|
.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<u32, &'static str> {
|
fn parse_color(s: String) -> Result<u32> {
|
||||||
if s.len() != 7 || &s[0..1] != "#" {
|
if s.len() != 7 || &s[0..1] != "#" {
|
||||||
Err("badly formated color.")
|
Err(anyhow!("badly formated color"))
|
||||||
} else {
|
} 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"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user