2021-12-14 17:33:42 +03:00
|
|
|
use crate::config::{ProfileExtra, ProfileResponse};
|
2021-12-17 21:29:54 +03:00
|
|
|
use std::{
|
|
|
|
str::FromStr,
|
|
|
|
time::{SystemTime, UNIX_EPOCH},
|
|
|
|
};
|
2021-12-12 21:29:48 +03:00
|
|
|
|
2021-12-13 21:32:42 +03:00
|
|
|
/// parse the string
|
2021-12-17 21:29:54 +03:00
|
|
|
fn parse_string<T: FromStr>(target: &str, key: &str) -> Option<T> {
|
2021-12-13 21:32:42 +03:00
|
|
|
match target.find(key) {
|
|
|
|
Some(idx) => {
|
|
|
|
let idx = idx + key.len();
|
|
|
|
let value = &target[idx..];
|
2021-12-17 21:29:54 +03:00
|
|
|
match match value.split(';').nth(0) {
|
|
|
|
Some(value) => value.trim().parse(),
|
|
|
|
None => value.trim().parse(),
|
|
|
|
} {
|
|
|
|
Ok(r) => Some(r),
|
|
|
|
Err(_) => None,
|
2021-12-13 21:32:42 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
None => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-14 17:33:42 +03:00
|
|
|
/// fetch and parse the profile
|
|
|
|
pub async fn fetch_profile(url: &str) -> Option<ProfileResponse> {
|
|
|
|
let resp = match reqwest::get(url).await {
|
|
|
|
Ok(res) => res,
|
|
|
|
Err(_) => return None,
|
|
|
|
};
|
|
|
|
let header = resp.headers();
|
2021-12-12 21:29:48 +03:00
|
|
|
|
2021-12-13 19:40:41 +03:00
|
|
|
// parse the Subscription Userinfo
|
2021-12-13 21:32:42 +03:00
|
|
|
let extra = {
|
2021-12-19 09:07:13 +03:00
|
|
|
let sub_info = match header.get("Subscription-Userinfo") {
|
|
|
|
Some(value) => value.to_str().unwrap_or(""),
|
|
|
|
None => "",
|
|
|
|
};
|
2021-12-13 21:32:42 +03:00
|
|
|
|
|
|
|
ProfileExtra {
|
2021-12-17 21:29:54 +03:00
|
|
|
upload: parse_string(sub_info, "upload=").unwrap_or(0),
|
|
|
|
download: parse_string(sub_info, "download=").unwrap_or(0),
|
|
|
|
total: parse_string(sub_info, "total=").unwrap_or(0),
|
|
|
|
expire: parse_string(sub_info, "expire=").unwrap_or(0),
|
2021-12-12 21:29:48 +03:00
|
|
|
}
|
2021-12-13 21:32:42 +03:00
|
|
|
};
|
|
|
|
|
2021-12-14 17:33:42 +03:00
|
|
|
// parse the `name` and `file`
|
|
|
|
let (name, file) = {
|
|
|
|
let now = SystemTime::now()
|
|
|
|
.duration_since(UNIX_EPOCH)
|
|
|
|
.unwrap()
|
|
|
|
.as_secs();
|
|
|
|
let file = format!("{}.yaml", now);
|
|
|
|
let name = header.get("Content-Disposition").unwrap().to_str().unwrap();
|
2021-12-17 21:29:54 +03:00
|
|
|
let name = parse_string::<String>(name, "filename=");
|
2021-12-14 17:33:42 +03:00
|
|
|
|
|
|
|
match name {
|
2021-12-17 21:29:54 +03:00
|
|
|
Some(f) => (f, file),
|
2021-12-14 17:33:42 +03:00
|
|
|
None => (file.clone(), file),
|
2021-12-12 21:29:48 +03:00
|
|
|
}
|
2021-12-13 21:32:42 +03:00
|
|
|
};
|
2021-12-12 21:29:48 +03:00
|
|
|
|
2021-12-14 17:33:42 +03:00
|
|
|
// get the data
|
|
|
|
let data = match resp.text_with_charset("utf-8").await {
|
|
|
|
Ok(d) => d,
|
|
|
|
Err(_) => return None,
|
2021-12-13 21:32:42 +03:00
|
|
|
};
|
2021-12-12 21:29:48 +03:00
|
|
|
|
2021-12-14 17:33:42 +03:00
|
|
|
Some(ProfileResponse {
|
|
|
|
file,
|
|
|
|
name,
|
|
|
|
data,
|
|
|
|
extra,
|
|
|
|
})
|
2021-12-12 21:29:48 +03:00
|
|
|
}
|
2021-12-13 21:32:42 +03:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_parse_value() {
|
|
|
|
let test_1 = "upload=111; download=2222; total=3333; expire=444";
|
|
|
|
let test_2 = "attachment; filename=Clash.yaml";
|
|
|
|
|
2021-12-17 21:29:54 +03:00
|
|
|
assert_eq!(parse_string::<usize>(test_1, "upload=").unwrap(), 111);
|
|
|
|
assert_eq!(parse_string::<usize>(test_1, "download=").unwrap(), 2222);
|
|
|
|
assert_eq!(parse_string::<usize>(test_1, "total=").unwrap(), 3333);
|
|
|
|
assert_eq!(parse_string::<usize>(test_1, "expire=").unwrap(), 444);
|
|
|
|
assert_eq!(
|
|
|
|
parse_string::<String>(test_2, "filename=").unwrap(),
|
|
|
|
format!("Clash.yaml")
|
|
|
|
);
|
2021-12-13 21:32:42 +03:00
|
|
|
|
2021-12-17 21:29:54 +03:00
|
|
|
assert_eq!(parse_string::<usize>(test_1, "aaa="), None);
|
|
|
|
assert_eq!(parse_string::<usize>(test_1, "upload1="), None);
|
|
|
|
assert_eq!(parse_string::<usize>(test_1, "expire1="), None);
|
|
|
|
assert_eq!(parse_string::<usize>(test_2, "attachment="), None);
|
2021-12-13 21:32:42 +03:00
|
|
|
}
|