use reqwest::StatusCode;
use crate::{
client::MarzbanAPIClient,
error::ApiError,
models::{
errors::HTTPValidationError,
user::{UserResponse, UserUsagesResponse},
},
};
impl MarzbanAPIClient {
pub async fn user_subscription(&self, user_token: impl AsRef<str>) -> Result<String, ApiError> {
let url = format!("{}/sub/{}", self.inner.base_url, user_token.as_ref());
let response = self
.send_with_auth_retry(|| async {
self.prepare_request(reqwest::Method::GET, url.clone())
})
.await?;
match response.status() {
StatusCode::OK => response.text().await.map_err(ApiError::NetworkError),
StatusCode::UNPROCESSABLE_ENTITY => {
let error_response = response.json::<HTTPValidationError>().await?;
Err(ApiError::ApiResponseError(format!(
"Validation Error: {error_response:?}"
)))
}
_ => Err(ApiError::UnexpectedResponse),
}
}
pub async fn user_subscription_info(
&self,
user_token: impl AsRef<str>,
) -> Result<UserResponse, ApiError> {
let url = format!("{}/sub/{}/info", self.inner.base_url, user_token.as_ref());
let response = self
.send_with_auth_retry(|| async {
self.prepare_request(reqwest::Method::GET, url.clone())
})
.await?;
match response.status() {
StatusCode::OK => response
.json::<UserResponse>()
.await
.map_err(ApiError::NetworkError),
StatusCode::UNPROCESSABLE_ENTITY => {
let error_response = response.json::<HTTPValidationError>().await?;
Err(ApiError::ApiResponseError(format!(
"Validation Error: {error_response:?}"
)))
}
_ => Err(ApiError::UnexpectedResponse),
}
}
pub async fn user_get_usage(
&self,
user_token: impl AsRef<str>,
start: Option<impl Into<String>>,
end: Option<impl Into<String>>,
) -> Result<UserUsagesResponse, ApiError> {
let url = format!("{}/sub/{}/usage", self.inner.base_url, user_token.as_ref());
let mut params = Vec::new();
if let Some(value) = start {
params.push(("start", value.into()))
}
if let Some(value) = end {
params.push(("end", value.into()))
}
let response = self
.send_with_auth_retry(|| async {
self.prepare_request(reqwest::Method::GET, url.clone())
.query(¶ms)
})
.await?;
match response.status() {
StatusCode::OK => response
.json::<UserUsagesResponse>()
.await
.map_err(ApiError::NetworkError),
StatusCode::UNPROCESSABLE_ENTITY => {
let error_response = response.json::<HTTPValidationError>().await?;
Err(ApiError::ApiResponseError(format!(
"Validation Error: {error_response:?}"
)))
}
_ => Err(ApiError::UnexpectedResponse),
}
}
pub async fn user_subscription_with_client_type(
&self,
user_token: impl AsRef<str>,
client_type: ClientTypes,
) -> Result<String, ApiError> {
let url = format!(
"{}/sub/{}/{}",
self.inner.base_url,
user_token.as_ref(),
client_type
);
let response = self
.send_with_auth_retry(|| async {
self.prepare_request(reqwest::Method::GET, url.clone())
})
.await?;
match response.status() {
StatusCode::OK => response.text().await.map_err(ApiError::NetworkError),
StatusCode::UNPROCESSABLE_ENTITY => {
let error_response = response.json::<HTTPValidationError>().await?;
Err(ApiError::ApiResponseError(format!(
"Validation Error: {error_response:?}"
)))
}
_ => Err(ApiError::UnexpectedResponse),
}
}
}
pub enum ClientTypes {
SingBox,
ClashMeta,
Clash,
Outline,
V2Ray,
V2RayJSON,
}
impl std::fmt::Display for ClientTypes {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ClientTypes::SingBox => write!(f, "sing-box"),
ClientTypes::ClashMeta => write!(f, "clash-meta"),
ClientTypes::Clash => write!(f, "clash"),
ClientTypes::Outline => write!(f, "outline"),
ClientTypes::V2Ray => write!(f, "v2ray"),
ClientTypes::V2RayJSON => write!(f, "v2ray-json"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn correct_client_types() {
assert_eq!(ClientTypes::Clash.to_string(), "clash")
}
}