1
0
mirror of https://github.com/pcvolkmer/arsnova-client.git synced 2025-04-19 19:16:51 +00:00

feat: improve error handling

This commit is contained in:
Paul-Christian Volkmer 2023-12-20 14:00:41 +01:00
parent 320fe0fa97
commit c37a75cfcf
2 changed files with 66 additions and 26 deletions

View File

@ -64,20 +64,20 @@ async fn main() -> Result<(), ()> {
return Err(()); 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::<Feedback>(10); let (tx, rx) = channel::<Feedback>(10);
let l1 = client.on_feedback_changed(&cli.room, FeedbackHandler::Sender(tx.clone()));
let _ = tx let _ = tx
.clone() .clone()
.send(client.get_feedback(&cli.room).await.unwrap()) .send(client.get_feedback(&cli.room).await.unwrap())
.await; .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 room_info = client.get_room_info(&cli.room).await.map_err(|_| ())?;
let title = format!("Live Feedback: {} ({})", room_info.name, room_info.short_id); let title = format!("Live Feedback: {} ({})", room_info.name, room_info.short_id);

View File

@ -17,10 +17,12 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
use std::error;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::time::Duration; use std::time::Duration;
use futures_util::{SinkExt, StreamExt}; use futures_util::{SinkExt, StreamExt};
use reqwest::StatusCode;
use serde::Deserialize; use serde::Deserialize;
use tokio::join; use tokio::join;
use tokio::sync::mpsc::Sender; use tokio::sync::mpsc::Sender;
@ -28,6 +30,8 @@ use tokio_tungstenite::connect_async;
use tokio_tungstenite::tungstenite::Message; use tokio_tungstenite::tungstenite::Message;
use url::Url; use url::Url;
use crate::client::ClientError::{ConnectionError, LoginError, ParserError, RoomNotFoundError};
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
struct LoginResponse { struct LoginResponse {
#[serde(rename = "token")] #[serde(rename = "token")]
@ -174,6 +178,27 @@ pub enum FeedbackHandler {
Sender(Sender<Feedback>), Sender(Sender<Feedback>),
} }
#[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 { pub struct Client {
api_url: String, api_url: String,
http_client: reqwest::Client, http_client: reqwest::Client,
@ -181,11 +206,11 @@ pub struct Client {
} }
impl Client { impl Client {
pub fn new(api_url: &str) -> Result<Client, ()> { pub fn new(api_url: &str) -> Result<Client, ClientError> {
let client = reqwest::Client::builder() let client = reqwest::Client::builder()
.user_agent(format!("arsnova-cli-client/{}", env!("CARGO_PKG_VERSION"))) .user_agent(format!("arsnova-cli-client/{}", env!("CARGO_PKG_VERSION")))
.build() .build()
.map_err(|_| ())?; .map_err(|_| ConnectionError)?;
Ok(Client { Ok(Client {
api_url: api_url.to_string(), 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 match self
.http_client .http_client
.post(format!("{}/auth/login/guest", self.api_url)) .post(format!("{}/auth/login/guest", self.api_url))
@ -206,13 +231,13 @@ impl Client {
self.token = Some(res.token); self.token = Some(res.token);
Ok(()) Ok(())
} }
Err(_) => Err(()), Err(_) => Err(LoginError),
}, },
Err(_) => Err(()), Err(_) => Err(ConnectionError),
} }
} }
pub async fn get_room_info(&self, short_id: &str) -> Result<RoomInfo, ()> { pub async fn get_room_info(&self, short_id: &str) -> Result<RoomInfo, ClientError> {
let token = self.token.as_ref().unwrap(); let token = self.token.as_ref().unwrap();
let membership_response = match self let membership_response = match self
@ -228,10 +253,16 @@ impl Client {
.send() .send()
.await .await
{ {
Ok(res) => res.json::<MembershipResponse>().await.unwrap(), Ok(res) => match res.status() {
Err(err) => { StatusCode::OK => res
eprintln!("{}", err); .json::<MembershipResponse>()
return Err(()); .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<Feedback, ()> { pub async fn get_feedback(&self, short_id: &str) -> Result<Feedback, ClientError> {
let room_info = self.get_room_info(short_id).await?; let room_info = self.get_room_info(short_id).await?;
let res = self match self
.http_client .http_client
.get(format!("{}/room/{}/survey", self.api_url, room_info.id)) .get(format!("{}/room/{}/survey", self.api_url, room_info.id))
.bearer_auth(self.token.as_ref().unwrap_or(&"".to_string()).to_string()) .bearer_auth(self.token.as_ref().unwrap_or(&"".to_string()).to_string())
.send() .send()
.await .await
.map_err(|_| ())?; {
Ok(res) => match res.status() {
Ok(Feedback::from_values(res.json::<Vec<u16>>().await.unwrap())) StatusCode::OK => Ok(Feedback::from_values(
res.json::<Vec<u16>>()
.await
.map_err(|err| ParserError(err.to_string()))?,
)),
StatusCode::NOT_FOUND => Err(RoomNotFoundError),
_ => Err(ConnectionError),
},
Err(_) => Err(ConnectionError),
}
} }
pub async fn on_feedback_changed( pub async fn on_feedback_changed(
&self, &self,
short_id: &str, short_id: &str,
handler: FeedbackHandler, handler: FeedbackHandler,
) -> Result<(), ()> { ) -> Result<(), ClientError> {
let room_info = self.get_room_info(short_id).await?; let room_info = self.get_room_info(short_id).await?;
let ws_url = self.api_url.replace("http", "ws"); let ws_url = self.api_url.replace("http", "ws");
let (socket, _) = connect_async(Url::parse(&format!("{}/ws/websocket", ws_url)).unwrap()) let (socket, _) = connect_async(Url::parse(&format!("{}/ws/websocket", ws_url)).unwrap())
.await .await
.map_err(|_| ())?; .map_err(|_| ConnectionError)?;
let (mut write, read) = socket.split(); let (mut write, read) = socket.split();
@ -285,7 +325,7 @@ impl Client {
.await .await
{ {
Ok(_) => {} Ok(_) => {}
Err(_) => return Err(()), Err(_) => return Err(ConnectionError),
} }
let jh1 = read.for_each(|msg| async { let jh1 = read.for_each(|msg| async {
@ -317,6 +357,6 @@ impl Client {
return Ok(()); return Ok(());
} }
Err(()) Err(ConnectionError)
} }
} }