rust_freely/client/
client.rs1pub mod api_client {
3 use serde_derive::{Deserialize, Serialize};
4
5 use crate::{api_handlers::{CollectionHandler, PostHandler, UserHandler}, api_models, api_wrapper::Api};
6
7 #[derive(Clone, Serialize, Deserialize, Debug)]
8 pub enum Auth {
10 Token(String),
12
13 Login{
15 username: String,
17
18 password: String
20 }
21 }
22
23 #[derive(Clone, Serialize, Deserialize, Debug)]
24 pub struct RequestError {
26 pub code: u16,
28
29 pub reason: Option<String>
31 }
32
33 #[derive(Clone, Serialize, Deserialize, Debug)]
34 #[serde(tag = "type")]
35 pub enum ApiError {
37 Request{
39 error: RequestError
41 },
42
43 AuthenticationError{},
45
46 UnknownError{},
48
49 UrlError{},
51
52 ParseError{
54 text: String
56 },
57
58 ConnectionError{},
60
61 LoggedOut{},
63
64 UsageError{}
66 }
67
68
69 #[derive(Clone, Serialize, Deserialize, Debug)]
70 pub struct Client {
72 _base_url: String,
73 _token: Option<String>,
74 }
75
76 impl Client {
77 pub fn new(base: String) -> Self {
79 Client { _base_url: base, _token: None }
80 }
81
82 pub async fn authenticate(&mut self, auth: Auth) -> Result<Self, ApiError> {
84 match auth {
85 Auth::Token(token) => {
86 self._token = Some(token);
87 Ok(self.clone())
88 },
89 Auth::Login { username, password } => {
90 match self.api().post::<api_models::responses::Login, _>("/auth/login", Some(api_models::requests::Login {alias: username, pass: password})).await {
91 Ok(data) => {
92 self._token = Some(data.access_token);
93 Ok(self.clone())
94 },
95 Err(e) => Err(e)
96 }
97 }
98 }
99 }
100
101 pub async fn logout(&mut self) -> Result<Self, ApiError> {
103 if self.is_authenticated() {
104 match self.api().delete("/auth/me").await {
105 Ok(_) => {
106 self._token = None;
107 Ok(self.clone())
108 },
109 Err(e) => Err(e)
110 }
111 } else {
112 Err(ApiError::LoggedOut { })
113 }
114 }
115
116 pub fn url(&self) -> String {
118 self._base_url.clone()
119 }
120
121 pub fn token(&self) -> Option<String> {
123 self._token.clone()
124 }
125
126 pub fn is_authenticated(&self) -> bool {
128 self._token.is_some()
129 }
130
131 pub fn api(&self) -> Api {
133 Api::new(self.clone())
134 }
135
136 pub async fn user(&self) -> Result<UserHandler, ApiError> {
138 if self.is_authenticated() {
139 Ok(UserHandler::new(self.clone()).await)
140 } else {
141 Err(ApiError::LoggedOut { })
142 }
143 }
144
145 pub fn posts(&self) -> PostHandler {
147 PostHandler::new(self.clone())
148 }
149
150 pub fn collections(&self) -> CollectionHandler {
152 CollectionHandler::new(self.clone())
153 }
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use std::{thread::sleep, time::Duration};
160
161 use super::*;
162 use api_client::{Auth, Client};
163 use tokio_test;
164
165 macro_rules! aw {
166 ($e:expr) => {
167 tokio_test::block_on($e)
168 };
169 }
170
171 fn anon() -> Client {
172 Client::new("http://0.0.0.0:8080".to_string())
173 }
174
175 async fn auth() -> Client {
176 Client::new("http://0.0.0.0:8080".to_string()).authenticate(Auth::Login { username: "username".to_string(), password: "password".to_string() }).await.unwrap()
177 }
178
179 #[test]
180 fn eq_url() {
181 assert_eq!(anon().url(), "http://0.0.0.0:8080".to_string());
182 }
183
184 #[test]
185 fn anon_no_token() {
186 assert!(!anon().is_authenticated());
187 }
188
189 #[test]
190 fn auth_has_token() {
191 assert!(aw!(auth()).is_authenticated())
192 }
193
194 #[test]
195 fn auth_bad_login() {
196 assert!(aw!(anon().authenticate(Auth::Login { username: "usernameee".to_string(), password: "passwordeee".to_string() })).is_err())
197 }
198
199 #[test]
200 fn auth_logout() {
201 let mut authed = aw!(auth());
202 println!("{:?}", authed.clone().token());
203 sleep(Duration::from_secs(2));
204 let logged_out = aw!(authed.logout());
205
206 assert!(!logged_out.unwrap().is_authenticated());
207 }
208
209}