use serde::{Deserialize, Serialize};
use std::time::{SystemTime, UNIX_EPOCH};
pub mod authorization;
pub mod token_provider;
pub use self::authorization::Authorization;
pub use self::token_provider::{TokenProvider, TokenProviderConfig};
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct DeviceCodeResponse {
pub device_code: String,
pub user_code: String,
pub verification_url: String,
pub qrcode_url: String,
pub interval: u32,
pub expires_in: u32,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct DeviceCode {
pub device_code: String,
pub user_code: String,
pub verification_url: String,
pub qrcode_url: String,
pub interval: u32,
pub expires_at: u64,
}
impl From<DeviceCodeResponse> for DeviceCode {
fn from(response: DeviceCodeResponse) -> Self {
let expires_at = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs()
+ response.expires_in as u64;
DeviceCode {
device_code: response.device_code,
user_code: response.user_code,
verification_url: response.verification_url,
qrcode_url: response.qrcode_url,
interval: response.interval,
expires_at,
}
}
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct AccessTokenResponse {
pub access_token: String,
pub expires_in: u64,
pub refresh_token: String,
pub scope: String,
pub session_key: String,
pub session_secret: String,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct AccessToken {
pub access_token: String,
pub expires_in: u64,
pub refresh_token: String,
pub scope: String,
pub session_key: String,
pub session_secret: String,
pub acquired_at: u64,
}
impl From<AccessTokenResponse> for AccessToken {
fn from(response: AccessTokenResponse) -> Self {
let acquired_at = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
AccessToken {
access_token: response.access_token,
expires_in: response.expires_in,
refresh_token: response.refresh_token,
scope: response.scope,
session_key: response.session_key,
session_secret: response.session_secret,
acquired_at,
}
}
}
impl AccessToken {
pub fn new(
access_token: String,
refresh_token: String,
expires_in: u64,
scope: String,
) -> Self {
let acquired_at = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
AccessToken {
access_token,
refresh_token,
expires_in,
scope,
session_key: String::new(),
session_secret: String::new(),
acquired_at,
}
}
pub fn with_all(
access_token: String,
refresh_token: String,
expires_in: u64,
scope: String,
session_key: String,
session_secret: String,
acquired_at: u64,
) -> Self {
AccessToken {
access_token,
refresh_token,
expires_in,
scope,
session_key,
session_secret,
acquired_at,
}
}
pub fn is_expired(&self) -> bool {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
now >= self.acquired_at + self.expires_in - 60
}
pub fn remaining_seconds(&self) -> u64 {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
let expires_at = self.acquired_at + self.expires_in;
expires_at.saturating_sub(now)
}
pub fn validate(&self) -> TokenStatus {
let remaining = self.remaining_seconds();
if remaining == 0 {
TokenStatus::Expired
} else if remaining <= 300 {
TokenStatus::ExpiringSoon
} else {
TokenStatus::Valid
}
}
pub fn is_valid(&self) -> bool {
matches!(self.validate(), TokenStatus::Valid)
}
pub fn expires_at(&self) -> u64 {
self.acquired_at + self.expires_in
}
}
#[cfg(test)]
mod tests {
use super::*;
fn create_test_token(acquired_at: u64, expires_in: u64) -> AccessToken {
AccessToken {
access_token: "test_token".to_string(),
refresh_token: "test_refresh".to_string(),
expires_in,
scope: "basic netdisk".to_string(),
session_key: String::new(),
session_secret: String::new(),
acquired_at,
}
}
#[test]
fn test_token_new() {
let token = AccessToken::new(
"access".to_string(),
"refresh".to_string(),
3600,
"basic netdisk".to_string(),
);
assert_eq!(token.access_token, "access");
assert_eq!(token.refresh_token, "refresh");
assert_eq!(token.expires_in, 3600);
assert!(token.session_key.is_empty());
}
#[test]
fn test_token_with_all() {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
let token = AccessToken::with_all(
"access".to_string(),
"refresh".to_string(),
7200,
"netdisk".to_string(),
"session_key".to_string(),
"session_secret".to_string(),
now,
);
assert_eq!(token.scope, "netdisk");
assert_eq!(token.session_key, "session_key");
assert_eq!(token.session_secret, "session_secret");
assert_eq!(token.acquired_at, now);
}
#[test]
fn test_token_is_expired() {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
let valid_token = create_test_token(now, 3600);
assert!(!valid_token.is_expired());
let expired_token = create_test_token(now - 7200, 3600);
assert!(expired_token.is_expired());
}
#[test]
fn test_token_validate() {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
let valid_token = create_test_token(now, 3600);
assert_eq!(valid_token.validate(), TokenStatus::Valid);
let expiring_soon_token = create_test_token(now, 200);
assert_eq!(expiring_soon_token.validate(), TokenStatus::ExpiringSoon);
let expired_token = create_test_token(now - 4000, 3600);
assert_eq!(expired_token.validate(), TokenStatus::Expired);
}
#[test]
fn test_token_is_valid() {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
let valid_token = create_test_token(now, 3600);
assert!(valid_token.is_valid());
let expired_token = create_test_token(now - 4000, 3600);
assert!(!expired_token.is_valid());
}
#[test]
fn test_token_remaining_seconds() {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
let token = create_test_token(now, 1000);
let remaining = token.remaining_seconds();
assert!((990..=1000).contains(&remaining));
let expired_token = create_test_token(now - 100, 50);
assert_eq!(expired_token.remaining_seconds(), 0);
}
#[test]
fn test_token_expires_at() {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
let token = create_test_token(now, 500);
assert_eq!(token.expires_at(), now + 500);
}
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
pub enum TokenStatus {
Valid,
ExpiringSoon,
Expired,
}
#[derive(Debug, Deserialize, Serialize, Default)]
pub struct ApiErrorResponse {
pub errno: Option<i32>,
pub errmsg: Option<String>,
#[serde(rename = "error_code")]
pub error_code: Option<i32>,
#[serde(rename = "error_msg")]
pub error_msg: Option<String>,
}
impl ApiErrorResponse {
pub fn get_errno(&self) -> i32 {
self.error_code.or(self.errno).unwrap_or(-1)
}
pub fn get_errmsg(&self) -> &str {
if let Some(msg) = &self.error_msg {
msg
} else if let Some(msg) = &self.errmsg {
msg
} else {
"Unknown error"
}
}
pub fn has_error(&self) -> bool {
self.errno.is_some()
|| self.error_code.is_some()
|| self.errmsg.is_some()
|| self.error_msg.is_some()
}
}
#[derive(Debug, Deserialize, Serialize)]
pub struct AuthErrorResponse {
pub error: String,
pub error_description: String,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct UserInfo {
pub baidu_name: String,
pub netdisk_name: String,
pub avatar_url: String,
pub vip_type: i32,
pub uk: u64,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct QuotaInfo {
pub total: u64,
pub used: u64,
pub free: u64,
}