diff --git a/examples/arsnova-client-tui.rs b/examples/arsnova-client-tui.rs index 063bc90..cdfb40a 100644 --- a/examples/arsnova-client-tui.rs +++ b/examples/arsnova-client-tui.rs @@ -64,20 +64,20 @@ async fn main() -> Result<(), ()> { return Err(()); } - stdout().execute(EnterAlternateScreen).map_err(|_| ())?; - enable_raw_mode().map_err(|_| ())?; - let mut terminal = Terminal::new(CrosstermBackend::new(stdout())).map_err(|_| ())?; - terminal.clear().map_err(|_| ())?; - let (tx, rx) = channel::(10); - let l1 = client.on_feedback_changed(&cli.room, FeedbackHandler::Sender(tx.clone())); - let _ = tx .clone() .send(client.get_feedback(&cli.room).await.unwrap()) .await; + stdout().execute(EnterAlternateScreen).map_err(|_| ())?; + enable_raw_mode().map_err(|_| ())?; + let mut terminal = Terminal::new(CrosstermBackend::new(stdout())).map_err(|_| ())?; + terminal.clear().map_err(|_| ())?; + + let l1 = client.on_feedback_changed(&cli.room, FeedbackHandler::Sender(tx.clone())); + let room_info = client.get_room_info(&cli.room).await.map_err(|_| ())?; let title = format!("Live Feedback: {} ({})", room_info.name, room_info.short_id); diff --git a/src/client.rs b/src/client.rs index 1e92b22..545d229 100644 --- a/src/client.rs +++ b/src/client.rs @@ -17,10 +17,12 @@ * along with this program. If not, see . */ +use std::error; use std::fmt::{Display, Formatter}; use std::time::Duration; use futures_util::{SinkExt, StreamExt}; +use reqwest::StatusCode; use serde::Deserialize; use tokio::join; use tokio::sync::mpsc::Sender; @@ -28,6 +30,8 @@ use tokio_tungstenite::connect_async; use tokio_tungstenite::tungstenite::Message; use url::Url; +use crate::client::ClientError::{ConnectionError, LoginError, ParserError, RoomNotFoundError}; + #[derive(Deserialize, Debug)] struct LoginResponse { #[serde(rename = "token")] @@ -174,6 +178,27 @@ pub enum FeedbackHandler { Sender(Sender), } +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum ClientError { + ConnectionError, + LoginError, + RoomNotFoundError, + ParserError(String), +} + +impl Display for ClientError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + ConnectionError => write!(f, "Cannot connect"), + LoginError => write!(f, "Cannot login"), + RoomNotFoundError => write!(f, "Requested room not found"), + ParserError(msg) => write!(f, "Cannot parse response: {}", msg), + } + } +} + +impl error::Error for ClientError {} + pub struct Client { api_url: String, http_client: reqwest::Client, @@ -181,11 +206,11 @@ pub struct Client { } impl Client { - pub fn new(api_url: &str) -> Result { + pub fn new(api_url: &str) -> Result { let client = reqwest::Client::builder() .user_agent(format!("arsnova-cli-client/{}", env!("CARGO_PKG_VERSION"))) .build() - .map_err(|_| ())?; + .map_err(|_| ConnectionError)?; Ok(Client { api_url: api_url.to_string(), @@ -194,7 +219,7 @@ impl Client { }) } - pub async fn guest_login(&mut self) -> Result<(), ()> { + pub async fn guest_login(&mut self) -> Result<(), ClientError> { match self .http_client .post(format!("{}/auth/login/guest", self.api_url)) @@ -206,13 +231,13 @@ impl Client { self.token = Some(res.token); Ok(()) } - Err(_) => Err(()), + Err(_) => Err(LoginError), }, - Err(_) => Err(()), + Err(_) => Err(ConnectionError), } } - pub async fn get_room_info(&self, short_id: &str) -> Result { + pub async fn get_room_info(&self, short_id: &str) -> Result { let token = self.token.as_ref().unwrap(); let membership_response = match self @@ -228,10 +253,16 @@ impl Client { .send() .await { - Ok(res) => res.json::().await.unwrap(), - Err(err) => { - eprintln!("{}", err); - return Err(()); + Ok(res) => match res.status() { + StatusCode::OK => res + .json::() + .await + .map_err(|err| ParserError(err.to_string()))?, + StatusCode::NOT_FOUND => return Err(RoomNotFoundError), + _ => return Err(ConnectionError), + }, + Err(_) => { + return Err(ConnectionError); } }; @@ -243,31 +274,40 @@ impl Client { }) } - pub async fn get_feedback(&self, short_id: &str) -> Result { + pub async fn get_feedback(&self, short_id: &str) -> Result { let room_info = self.get_room_info(short_id).await?; - let res = self + match self .http_client .get(format!("{}/room/{}/survey", self.api_url, room_info.id)) .bearer_auth(self.token.as_ref().unwrap_or(&"".to_string()).to_string()) .send() .await - .map_err(|_| ())?; - - Ok(Feedback::from_values(res.json::>().await.unwrap())) + { + Ok(res) => match res.status() { + StatusCode::OK => Ok(Feedback::from_values( + res.json::>() + .await + .map_err(|err| ParserError(err.to_string()))?, + )), + StatusCode::NOT_FOUND => Err(RoomNotFoundError), + _ => Err(ConnectionError), + }, + Err(_) => Err(ConnectionError), + } } pub async fn on_feedback_changed( &self, short_id: &str, handler: FeedbackHandler, - ) -> Result<(), ()> { + ) -> Result<(), ClientError> { let room_info = self.get_room_info(short_id).await?; let ws_url = self.api_url.replace("http", "ws"); let (socket, _) = connect_async(Url::parse(&format!("{}/ws/websocket", ws_url)).unwrap()) .await - .map_err(|_| ())?; + .map_err(|_| ConnectionError)?; let (mut write, read) = socket.split(); @@ -285,7 +325,7 @@ impl Client { .await { Ok(_) => {} - Err(_) => return Err(()), + Err(_) => return Err(ConnectionError), } let jh1 = read.for_each(|msg| async { @@ -317,6 +357,6 @@ impl Client { return Ok(()); } - Err(()) + Err(ConnectionError) } }