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

feat: apply typestate pattern to client

This prevents using the client without using guest login and without token.
This commit is contained in:
Paul-Christian Volkmer 2023-12-20 14:36:45 +01:00
parent b116457796
commit 9cdd68abed
3 changed files with 30 additions and 11 deletions

View File

@ -10,7 +10,7 @@ Create a client using and request guest login.
```rust
let client = Client::new("https://ars.particify.de/api").expect("client created");
client.guest_login().await;
let client = client.guest_login().await.expect("logged in");
```
### Request room information

View File

@ -55,14 +55,12 @@ pub struct Cli {
async fn main() -> Result<(), ()> {
let cli = Cli::parse();
let mut client = match Client::new(&cli.url) {
let client = match Client::new(&cli.url) {
Ok(client) => client,
Err(_) => return Err(()),
};
if client.guest_login().await.is_err() {
return Err(());
}
let client = client.guest_login().await.map_err(|_| ())?;
let (tx, rx) = channel::<Feedback>(10);

View File

@ -19,6 +19,7 @@
use std::error;
use std::fmt::{Display, Formatter};
use std::marker::PhantomData;
use std::time::Duration;
use futures_util::{SinkExt, StreamExt};
@ -199,10 +200,14 @@ impl Display for ClientError {
impl error::Error for ClientError {}
pub struct Client {
pub struct LoggedIn;
pub struct LoggedOut;
pub struct Client<State = LoggedOut> {
api_url: String,
http_client: reqwest::Client,
token: Option<String>,
state: PhantomData<State>,
}
impl Client {
@ -216,10 +221,13 @@ impl Client {
api_url: api_url.to_string(),
http_client: client,
token: None,
state: PhantomData::<LoggedOut>,
})
}
}
pub async fn guest_login(&mut self) -> Result<(), ClientError> {
impl Client<LoggedOut> {
pub async fn guest_login(self) -> Result<Client<LoggedIn>, ClientError> {
match self
.http_client
.post(format!("{}/auth/login/guest", self.api_url))
@ -227,15 +235,28 @@ impl Client {
.await
{
Ok(res) => match res.json::<LoginResponse>().await {
Ok(res) => {
self.token = Some(res.token);
Ok(())
}
Ok(res) => Ok(Client {
api_url: self.api_url,
http_client: self.http_client,
token: Some(res.token),
state: PhantomData::<LoggedIn>,
}),
Err(_) => Err(LoginError),
},
Err(_) => Err(ConnectionError),
}
}
}
impl Client<LoggedIn> {
pub fn logout(self) -> Client<LoggedOut> {
Client {
api_url: self.api_url,
http_client: self.http_client,
token: None,
state: PhantomData::<LoggedOut>,
}
}
pub async fn get_room_info(&self, short_id: &str) -> Result<RoomInfo, ClientError> {
let token = self.token.as_ref().unwrap();