videocall_meeting_client/
lib.rs1pub mod auth;
40pub mod error;
41pub mod meetings;
42pub mod participants;
43pub mod waiting_room;
44
45pub use error::ApiError;
46pub use videocall_meeting_types;
47
48use reqwest::Client;
49
50#[derive(Debug, Clone)]
52pub enum AuthMode {
53 Cookie,
56 Bearer(String),
59}
60
61#[derive(Debug, Clone)]
66pub struct MeetingApiClient {
67 base_url: String,
68 auth: AuthMode,
69 http: Client,
70}
71
72impl MeetingApiClient {
73 pub fn new(base_url: &str, auth: AuthMode) -> Self {
80 Self {
81 base_url: base_url.trim_end_matches('/').to_string(),
82 auth,
83 http: Client::new(),
84 }
85 }
86
87 pub fn set_bearer_token(&mut self, token: String) {
90 self.auth = AuthMode::Bearer(token);
91 }
92
93 pub(crate) fn get(&self, path: &str) -> reqwest::RequestBuilder {
95 self.apply_auth(self.http.get(self.url(path)))
96 }
97
98 pub(crate) fn post(&self, path: &str) -> reqwest::RequestBuilder {
100 self.apply_auth(self.http.post(self.url(path)))
101 }
102
103 pub(crate) fn delete(&self, path: &str) -> reqwest::RequestBuilder {
105 self.apply_auth(self.http.delete(self.url(path)))
106 }
107
108 fn url(&self, path: &str) -> String {
109 format!("{}{}", self.base_url, path)
110 }
111
112 fn apply_auth(&self, builder: reqwest::RequestBuilder) -> reqwest::RequestBuilder {
113 match &self.auth {
114 AuthMode::Cookie => {
115 #[cfg(target_arch = "wasm32")]
116 {
117 builder.fetch_credentials_include()
118 }
119 #[cfg(not(target_arch = "wasm32"))]
120 {
121 builder
122 }
123 }
124 AuthMode::Bearer(token) => {
125 builder.header(reqwest::header::AUTHORIZATION, format!("Bearer {token}"))
126 }
127 }
128 }
129}
130
131pub(crate) async fn parse_api_response<T: serde::de::DeserializeOwned + serde::Serialize>(
134 response: reqwest::Response,
135) -> Result<T, ApiError> {
136 let status = response.status().as_u16();
137 match status {
138 200 | 201 => {
139 let wrapper: videocall_meeting_types::responses::APIResponse<T> =
140 response.json().await?;
141 Ok(wrapper.result)
142 }
143 401 => Err(ApiError::NotAuthenticated),
144 403 => {
145 let text = response.text().await.unwrap_or_default();
146 Err(ApiError::Forbidden(text))
147 }
148 404 => {
149 let text = response.text().await.unwrap_or_default();
150 Err(ApiError::NotFound(text))
151 }
152 400 => {
153 let text = response.text().await.unwrap_or_default();
154 if text.contains("MEETING_NOT_ACTIVE") {
155 Err(ApiError::MeetingNotActive)
156 } else {
157 Err(ApiError::ServerError {
158 status: 400,
159 body: text,
160 })
161 }
162 }
163 _ => {
164 let text = response.text().await.unwrap_or_default();
165 Err(ApiError::ServerError { status, body: text })
166 }
167 }
168}
169
170pub(crate) async fn parse_status_only(response: reqwest::Response) -> Result<(), ApiError> {
172 let status = response.status().as_u16();
173 match status {
174 200..=299 => Ok(()),
175 401 => Err(ApiError::NotAuthenticated),
176 403 => {
177 let text = response.text().await.unwrap_or_default();
178 Err(ApiError::Forbidden(text))
179 }
180 404 => {
181 let text = response.text().await.unwrap_or_default();
182 Err(ApiError::NotFound(text))
183 }
184 _ => {
185 let text = response.text().await.unwrap_or_default();
186 Err(ApiError::ServerError { status, body: text })
187 }
188 }
189}