use std::fmt::Debug;
use reqwest::{Client, Url};
use serde::{Deserialize, Serialize};
use crate::{
sessionrequest::ExtendedIrmaRequest, Error, IrmaRequest, SessionResult, SessionStatus,
SessionType,
};
#[derive(Clone, Debug)]
enum AuthMethod {
None,
Token(TokenSecret),
}
#[derive(Clone)]
struct TokenSecret {
token: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Qr {
#[doc(hidden)]
pub u: String,
#[doc(hidden)]
pub irmaqr: SessionType,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SessionData {
#[serde(rename = "sessionPtr")]
pub session_ptr: Qr,
pub token: SessionToken,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(test, derive(PartialEq))]
pub struct SessionToken(pub String);
impl Debug for TokenSecret {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TokenSecret").finish()
}
}
#[derive(Debug, Clone)]
pub struct IrmaClient {
url: Url,
client: Client,
authmethod: AuthMethod,
}
impl IrmaClient {
pub fn new(url: &str) -> Result<IrmaClient, Error> {
Ok(IrmaClient {
url: Url::parse(url)?,
client: Client::new(),
authmethod: AuthMethod::None,
})
}
pub async fn request(&self, request: &IrmaRequest) -> Result<SessionData, Error> {
let req = self.client.post(self.url.join("session").unwrap());
let req = match &self.authmethod {
AuthMethod::None => req,
AuthMethod::Token(TokenSecret { token }) => req.header("Authorization", token),
};
Ok(req
.json(request)
.send()
.await?
.error_for_status()?
.json::<SessionData>()
.await?)
}
pub async fn request_extended(
&self,
request: &ExtendedIrmaRequest,
) -> Result<SessionData, Error> {
let req = self.client.post(self.url.join("session").unwrap());
let req = match &self.authmethod {
AuthMethod::None => req,
AuthMethod::Token(TokenSecret { token }) => req.header("Authorization", token),
};
Ok(req
.json(request)
.send()
.await?
.error_for_status()?
.json::<SessionData>()
.await?)
}
pub async fn status(&self, token: &SessionToken) -> Result<SessionStatus, Error> {
Ok(self
.client
.get(self.url.join(&format!("session/{}/status", token.0))?)
.send()
.await?
.error_for_status()?
.json::<SessionStatus>()
.await?)
}
pub async fn cancel(&self, token: &SessionToken) -> Result<(), Error> {
self.client
.delete(self.url.join(&format!("session/{}", token.0))?)
.send()
.await?
.error_for_status()?;
Ok(())
}
pub async fn result(&self, token: &SessionToken) -> Result<SessionResult, Error> {
let result = self
.client
.get(self.url.join(&format!("session/{}/result", token.0))?)
.send()
.await?
.error_for_status()?
.json::<SessionResult>()
.await?;
match result.status {
SessionStatus::Done => Ok(result),
SessionStatus::Cancelled => Err(Error::SessionCancelled),
SessionStatus::Timeout => Err(Error::SessionTimedOut),
status => Err(Error::SessionNotFinished(status)),
}
}
}
pub struct IrmaClientBuilder {
url: Url,
authmethod: AuthMethod,
}
impl IrmaClientBuilder {
pub fn new(url: &str) -> Result<IrmaClientBuilder, Error> {
Ok(IrmaClientBuilder {
url: Url::parse(url)?,
authmethod: AuthMethod::None,
})
}
pub fn token_authentication(mut self, token: String) -> IrmaClientBuilder {
self.authmethod = AuthMethod::Token(TokenSecret { token });
self
}
pub fn build(self) -> IrmaClient {
IrmaClient {
url: self.url,
client: Client::new(),
authmethod: self.authmethod,
}
}
}