1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2025-07-03 05:27:04 +02:00

Add the description of the chat-app exercise (#641)

* Adds a description of the async chat exercise
* Simplifies the use of Error in chat-async
* Links the solution to the async chat exercise
* Removes the elevator exercise
This commit is contained in:
rbehjati
2023-05-17 18:22:11 +01:00
committed by GitHub
parent 8406697449
commit 83663daaa2
16 changed files with 189 additions and 777 deletions

View File

@ -6,6 +6,5 @@ edition = "2021"
[dependencies]
futures-util = "0.3.28"
http = "0.2.9"
thiserror = "1.0.40"
tokio = { version = "1.28.1", features = ["net", "macros", "time", "rt", "rt-multi-thread", "io-std", "io-util"] }
tokio-websockets = "0.3.0"
tokio = { version = "1.28.1", features = ["full"] }
tokio-websockets = "0.3.2"

View File

@ -1,3 +1,18 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ANCHOR: setup
use futures_util::SinkExt;
use http::Uri;
use tokio::io::{AsyncBufReadExt, BufReader};
@ -12,6 +27,8 @@ async fn main() -> Result<(), tokio_websockets::Error> {
let stdin = tokio::io::stdin();
let mut stdin = BufReader::new(stdin);
// ANCHOR_END: setup
// Continuous loop for concurrently sending and receiving messages.
loop {
let mut line = String::new();
tokio::select! {

View File

@ -1,46 +1,42 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ANCHOR: setup
use futures_util::sink::SinkExt;
use std::error::Error;
use std::net::SocketAddr;
use thiserror::Error;
use tokio::net::{TcpListener, TcpStream};
use tokio::sync::broadcast::error::{RecvError, SendError};
use tokio::sync::broadcast::{channel, Sender};
use tokio_websockets::{Message, ServerBuilder, WebsocketStream};
// ANCHOR_END: setup
#[derive(Error, Debug)]
enum ServerError {
#[error("websocket error: {0}")]
Websocket(String),
#[error("io error: {0}")]
IO(#[from] std::io::Error),
#[error("broadcast channel SendError: {0}")]
SendError(#[from] SendError<String>),
#[error("broadcast channel RecvError: {0}")]
RecvError(#[from] RecvError),
}
// tokio_websockets Error types do not implement std::error::Error, so we make do by just capturing
// the debug format for the error.
impl From<tokio_websockets::Error> for ServerError {
fn from(err: tokio_websockets::Error) -> Self {
ServerError::Websocket(format!("{:?}", err))
}
}
impl From<tokio_websockets::proto::ProtocolError> for ServerError {
fn from(err: tokio_websockets::proto::ProtocolError) -> Self {
ServerError::Websocket(format!("{:?}", err))
}
}
// ANCHOR: handle_connection
async fn handle_connection(
addr: SocketAddr,
mut ws_stream: WebsocketStream<TcpStream>,
bcast_tx: Sender<String>,
) -> Result<(), ServerError> {
) -> Result<(), Box<dyn Error + Send + Sync>> {
// ANCHOR_END: handle_connection
ws_stream
.send(Message::text("Welcome to chat! Type a message".into()))
.await?;
let mut bcast_rx = bcast_tx.subscribe();
// A continuous loop for concurrently performing two tasks: (1) receiving
// messages from `ws_stream` and broadcasting them, and (2) receiving
// messages on `bcast_rx` and sending them to the client.
loop {
tokio::select! {
incoming = ws_stream.next() => {
@ -59,10 +55,11 @@ async fn handle_connection(
}
}
}
// ANCHOR: main
}
#[tokio::main]
async fn main() -> Result<(), ServerError> {
async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
let (bcast_tx, _) = channel(16);
let listener = TcpListener::bind("127.0.0.1:2000").await?;
@ -80,3 +77,4 @@ async fn main() -> Result<(), ServerError> {
});
}
}
// ANCHOR_END: main