Skip to main content

synd_client/client/
session.rs

1use std::fmt::Debug;
2
3use reqwest::StatusCode;
4use serde::{Serialize, de::DeserializeOwned};
5use synd_protocol::session::{
6    CloseSessionErrorResponse, CloseSessionRequest, CloseSessionResponse, OpenSessionErrorResponse,
7    OpenSessionRequest, OpenSessionResponse, RenewSessionErrorResponse, RenewSessionRequest,
8    RenewSessionResponse,
9};
10
11use super::Client;
12use crate::SyndApiError;
13
14enum SessionOutcome<T, E> {
15    Accepted(T),
16    Rejected(E),
17}
18
19trait SessionRequest: Serialize + Debug {
20    type Response: DeserializeOwned;
21    type Rejection: DeserializeOwned;
22
23    const PATH: &'static str;
24    const REJECTION_STATUS: StatusCode;
25    const UNEXPECTED_RESPONSE_CONTEXT: &'static str;
26
27    fn rejected(response: Self::Rejection) -> SyndApiError;
28
29    async fn decode_response(
30        response: reqwest::Response,
31    ) -> Result<SessionOutcome<Self::Response, Self::Rejection>, SyndApiError> {
32        let status = response.status();
33
34        if status.is_success() {
35            return response
36                .json()
37                .await
38                .map(SessionOutcome::Accepted)
39                .map_err(SyndApiError::DecodeResponse);
40        }
41        if status == Self::REJECTION_STATUS {
42            return response
43                .json()
44                .await
45                .map(SessionOutcome::Rejected)
46                .map_err(SyndApiError::DecodeResponse);
47        }
48
49        response
50            .error_for_status()
51            .map_err(SyndApiError::from_status_error)?;
52        Err(SyndApiError::UnexpectedResponse {
53            context: Self::UNEXPECTED_RESPONSE_CONTEXT,
54        })
55    }
56}
57
58impl SessionRequest for OpenSessionRequest {
59    type Response = OpenSessionResponse;
60    type Rejection = OpenSessionErrorResponse;
61
62    const PATH: &'static str = "/session/open";
63    const REJECTION_STATUS: StatusCode = StatusCode::CONFLICT;
64    const UNEXPECTED_RESPONSE_CONTEXT: &'static str = "session open";
65
66    fn rejected(response: Self::Rejection) -> SyndApiError {
67        SyndApiError::OpenSession(response)
68    }
69}
70
71impl SessionRequest for RenewSessionRequest {
72    type Response = RenewSessionResponse;
73    type Rejection = RenewSessionErrorResponse;
74
75    const PATH: &'static str = "/session/renew";
76    const REJECTION_STATUS: StatusCode = StatusCode::NOT_FOUND;
77    const UNEXPECTED_RESPONSE_CONTEXT: &'static str = "session renew";
78
79    fn rejected(response: Self::Rejection) -> SyndApiError {
80        SyndApiError::RenewSession(response)
81    }
82}
83
84impl SessionRequest for CloseSessionRequest {
85    type Response = CloseSessionResponse;
86    type Rejection = CloseSessionErrorResponse;
87
88    const PATH: &'static str = "/session/close";
89    const REJECTION_STATUS: StatusCode = StatusCode::NOT_FOUND;
90    const UNEXPECTED_RESPONSE_CONTEXT: &'static str = "session close";
91
92    fn rejected(response: Self::Rejection) -> SyndApiError {
93        SyndApiError::CloseSession(response)
94    }
95}
96
97impl Client {
98    pub async fn open_session(
99        &self,
100        request: OpenSessionRequest,
101    ) -> Result<OpenSessionResponse, SyndApiError> {
102        self.execute_session(request).await
103    }
104
105    pub async fn close_session(
106        &self,
107        request: CloseSessionRequest,
108    ) -> Result<CloseSessionResponse, SyndApiError> {
109        self.execute_session(request).await
110    }
111
112    pub async fn renew_session(
113        &self,
114        request: RenewSessionRequest,
115    ) -> Result<RenewSessionResponse, SyndApiError> {
116        self.execute_session(request).await
117    }
118
119    async fn execute_session<R>(&self, request: R) -> Result<R::Response, SyndApiError>
120    where
121        R: SessionRequest,
122    {
123        let response = self.send_session_request(&request).await?;
124        let outcome = R::decode_response(response).await?;
125
126        match outcome {
127            SessionOutcome::Accepted(response) => Ok(response),
128            SessionOutcome::Rejected(rejection) => Err(R::rejected(rejection)),
129        }
130    }
131
132    async fn send_session_request<R>(&self, request: &R) -> Result<reqwest::Response, SyndApiError>
133    where
134        R: SessionRequest,
135    {
136        let mut request = self
137            .client
138            .post(self.endpoint.join(R::PATH)?)
139            .json(request)
140            .build()
141            .map_err(SyndApiError::BuildRequest)?;
142        self.authentication
143            .apply_authorization_header(request.headers_mut())?;
144        self.client
145            .execute(request)
146            .await
147            .map_err(SyndApiError::from_send_error)
148    }
149}