smbcloud_network/
network.rs

1use {
2    log::{debug, error},
3    reqwest::{RequestBuilder, Response, StatusCode},
4    serde::de::DeserializeOwned,
5    smbcloud_model::{
6        account::SmbAuthorization,
7        error_codes::{ErrorCode, ErrorResponse},
8        login::AccountStatus,
9    },
10};
11
12//use std::time::Duration;
13#[cfg(debug_assertions)]
14const LOG_RESPONSE_BODY: bool = true; // You know what to do here.
15#[cfg(not(debug_assertions))]
16const LOG_RESPONSE_BODY: bool = false;
17
18/// Check if there is an active internet connection
19///
20/// This function attempts to connect to a reliable server (dns.google)
21/// with a short timeout. Returns true if the connection was successful.
22pub async fn check_internet_connection() -> bool {
23    debug!("Checking internet connection");
24    let client = reqwest::Client::builder()
25        //.timeout(Duration::from_secs(5)) Does not work on wasm32
26        .build();
27
28    if let Err(e) = client {
29        error!("Failed to create client for connectivity check: {:?}", e);
30        return false;
31    }
32
33    if let Ok(client) = client {
34        match client.get("https://dns.google").send().await {
35            Ok(response) => {
36                debug!(
37                    "Internet connection check successful: {}",
38                    response.status()
39                );
40                response.status().is_success()
41            }
42            Err(e) => {
43                error!("Internet connection check failed: {:?}", e);
44                false
45            }
46        }
47    } else {
48        false
49    }
50}
51
52pub async fn parse_error_response<T: DeserializeOwned>(
53    response: Response,
54) -> Result<T, ErrorResponse> {
55    let error_response_body = match response.text().await {
56        Ok(body) => body,
57        Err(e) => {
58            error!("Failed to get response body: {:?}", e);
59            return Err(ErrorResponse::Error {
60                error_code: ErrorCode::NetworkError,
61                message: ErrorCode::NetworkError.message(None).to_string(),
62            });
63        }
64    };
65
66    if LOG_RESPONSE_BODY {
67        println!();
68        println!("Parse Error >>>>");
69        println!("{:?}", serde_json::to_string_pretty(&error_response_body));
70        println!("Parse Error >>>>");
71        println!();
72    }
73
74    let error_response = match serde_json::from_str(&error_response_body) {
75        Ok(error_response) => error_response,
76        Err(e) => {
77            error!("Failed to parse error response: {:?}", e);
78            return Err(ErrorResponse::Error {
79                error_code: ErrorCode::ParseError,
80                message: ErrorCode::ParseError.message(None).to_string(),
81            });
82        }
83    };
84    // The parsing itself is succeed.
85    // Why is this an ErrorResponse.
86    Err(error_response)
87}
88
89pub async fn request_login(builder: RequestBuilder) -> Result<AccountStatus, ErrorResponse> {
90    let response = builder.send().await;
91    let response = match response {
92        Ok(response) => response,
93        Err(e) => {
94            error!("request_login: Failed to get response: {:?}", e);
95            return Err(ErrorResponse::Error {
96                error_code: ErrorCode::NetworkError,
97                message: ErrorCode::NetworkError.message(None).to_string(),
98            });
99        }
100    };
101
102    if LOG_RESPONSE_BODY {
103        println!();
104        println!("request_login: Parse >>>>");
105        println!("{:?}", &response.status());
106        println!("request_login: Parse >>>>");
107        println!();
108    }
109
110    match (response.status(), response.headers().get("Authorization")) {
111        (StatusCode::OK, Some(token)) => {
112            // Login successful, let's get the access token for real.
113            let access_token = match token.to_str() {
114                Ok(token) => token.to_string(),
115                Err(_) => {
116                    return Err(ErrorResponse::Error {
117                        error_code: ErrorCode::NetworkError,
118                        message: ErrorCode::NetworkError.message(None).to_string(),
119                    });
120                }
121            };
122            Ok(AccountStatus::Ready { access_token })
123        }
124        (StatusCode::OK, None) => {
125            // Silent login from oauth. Need improvement.
126            let error_response = match parse_error_response::<ErrorResponse>(response).await {
127                Ok(error) => error,
128                Err(_) => return Ok(AccountStatus::NotFound),
129            };
130            match error_response {
131                ErrorResponse::Error {
132                    error_code,
133                    message: _,
134                } => match error_code {
135                    ErrorCode::EmailNotVerified => Ok(AccountStatus::Incomplete {
136                        status: smbcloud_model::account::ErrorCode::EmailUnverified,
137                    }),
138                    ErrorCode::PasswordNotSet => Ok(AccountStatus::Incomplete {
139                        status: smbcloud_model::account::ErrorCode::PasswordNotSet,
140                    }),
141                    ErrorCode::Unknown => Ok(AccountStatus::Ready {
142                        access_token: "tokenization".to_string(),
143                    }),
144                    _ => Ok(AccountStatus::NotFound),
145                },
146            }
147        }
148        (StatusCode::NOT_FOUND, _) => {
149            // Account not found
150            Ok(AccountStatus::NotFound)
151        }
152        (StatusCode::UNPROCESSABLE_ENTITY, _) => {
153            // Account found but email not verified / password not set.
154            let result: SmbAuthorization = match response.json().await {
155                Ok(res) => res,
156                Err(_) => {
157                    return Err(ErrorResponse::Error {
158                        error_code: ErrorCode::NetworkError,
159                        message: ErrorCode::NetworkError.message(None).to_string(),
160                    });
161                }
162            };
163            // println!("Result: {:#?}", &result);
164            let error_code = match result.error_code {
165                Some(code) => code,
166                None => {
167                    return Err(ErrorResponse::Error {
168                        error_code: ErrorCode::NetworkError,
169                        message: ErrorCode::NetworkError.message(None).to_string(),
170                    });
171                }
172            };
173            Ok(AccountStatus::Incomplete { status: error_code })
174        }
175        _ => Err(ErrorResponse::Error {
176            error_code: ErrorCode::NetworkError,
177            message: ErrorCode::NetworkError.message(None).to_string(),
178        }),
179    }
180}
181
182pub async fn request<R: DeserializeOwned>(builder: RequestBuilder) -> Result<R, ErrorResponse> {
183    // Check internet connection before making the request
184    if !check_internet_connection().await {
185        error!("No internet connection available");
186        return Err(ErrorResponse::Error {
187            error_code: ErrorCode::NetworkError,
188            message: "No internet connection. Please check your network settings and try again."
189                .to_string(),
190        });
191    }
192
193    let response = builder.send().await;
194    let response = match response {
195        Ok(response) => response,
196        Err(e) => {
197            error!("Failed to get response: {:?}", e);
198            return Err(ErrorResponse::Error {
199                error_code: ErrorCode::NetworkError,
200                message: ErrorCode::NetworkError.message(None).to_string(),
201            });
202        }
203    };
204    let response = match response.status() {
205        reqwest::StatusCode::OK | reqwest::StatusCode::CREATED => response,
206        status => {
207            error!("Failed to get response: {:?}", status);
208            // This should handle parsing the error response.
209            return parse_error_response(response).await;
210        }
211    };
212
213    let response_body = match response.text().await {
214        Ok(body) => body,
215        Err(e) => {
216            error!("Failed to get response body: {:?}", e);
217            return Err(ErrorResponse::Error {
218                error_code: ErrorCode::NetworkError,
219                message: ErrorCode::NetworkError.message(None).to_string(),
220            });
221        }
222    };
223
224    if LOG_RESPONSE_BODY {
225        println!();
226        println!("Parse >>>>");
227        println!("{:?}", serde_json::to_string_pretty(&response_body));
228        println!("Parse >>>>");
229        println!();
230    }
231
232    let response = match serde_json::from_str::<R>(&response_body) {
233        Ok(response) => response,
234        Err(e) => {
235            error!("Failed to parse response: {:?}", e);
236            return Err(ErrorResponse::Error {
237                error_code: ErrorCode::ParseError,
238                message: e.to_string(),
239            });
240        }
241    };
242
243    Ok(response)
244}