use anyhow::{Result, anyhow};
use base64::{Engine as _, engine::general_purpose};
use chrono::Utc;
use hmac::{Hmac, Mac};
use sha2::Sha256;
use std::collections::BTreeMap;
pub fn parse_params_to_str(params: &BTreeMap<String, String>) -> String {
if params.is_empty() {
return "".to_string();
}
let mut s = String::from("?");
for (i, (k, v)) in params.iter().enumerate() {
if i > 0 {
s.push('&');
}
s.push_str(&format!("{k}={v}"));
}
s
}
pub fn get_timestamp() -> String {
Utc::now().timestamp_millis().to_string()
}
pub fn pre_hash(timestamp: &str, method: &str, request_path: &str, body: &str) -> String {
format!("{timestamp}{method}{request_path}{body}")
}
pub fn sign(pre_hash: &str, secret: &str) -> Result<String> {
let mut mac = Hmac::<Sha256>::new_from_slice(secret.as_bytes())
.map_err(|e| anyhow!("HMAC 初始化失败: {:?}", e))?;
mac.update(pre_hash.as_bytes());
let result = mac.finalize().into_bytes();
Ok(general_purpose::STANDARD.encode(result))
}
pub fn get_header(
api_key: &str,
sign: &str,
timestamp: &str,
passphrase: &str,
) -> Vec<(String, String)> {
vec![
("Content-Type".to_string(), "application/json".to_string()),
("ACCESS-KEY".to_string(), api_key.to_string()),
("ACCESS-SIGN".to_string(), sign.to_string()),
("ACCESS-TIMESTAMP".to_string(), timestamp.to_string()),
("ACCESS-PASSPHRASE".to_string(), passphrase.to_string()),
]
}
pub fn build_query(params: &BTreeMap<String, String>) -> String {
params
.iter()
.map(|(k, v)| format!("{}={}", k, v))
.collect::<Vec<_>>()
.join("&")
}