use std::time::Duration;
use crate::error::{Error, Result};
pub const DEFAULT_BASE_URL: &str = "https://ilinkai.weixin.qq.com/";
pub const DEFAULT_CDN_BASE_URL: &str = "https://novac2c.cdn.weixin.qq.com/c2c";
pub const DEFAULT_LONG_POLL_TIMEOUT: Duration = Duration::from_millis(35_000);
pub const DEFAULT_API_TIMEOUT: Duration = Duration::from_millis(15_000);
#[derive(Debug, Clone)]
pub struct WeixinConfig {
pub base_url: String,
pub cdn_base_url: String,
pub token: String,
pub route_tag: Option<u32>,
pub long_poll_timeout: Duration,
pub api_timeout: Duration,
}
#[derive(Debug, Default)]
#[must_use]
pub struct WeixinConfigBuilder {
base_url: Option<String>,
cdn_base_url: Option<String>,
token: Option<String>,
route_tag: Option<u32>,
long_poll_timeout: Option<Duration>,
api_timeout: Option<Duration>,
}
impl WeixinConfig {
pub fn builder() -> WeixinConfigBuilder {
WeixinConfigBuilder::default()
}
}
impl WeixinConfigBuilder {
pub fn base_url(mut self, url: impl Into<String>) -> Self {
self.base_url = Some(url.into());
self
}
pub fn cdn_base_url(mut self, url: impl Into<String>) -> Self {
self.cdn_base_url = Some(url.into());
self
}
pub fn token(mut self, token: impl Into<String>) -> Self {
self.token = Some(token.into());
self
}
pub fn route_tag(mut self, tag: u32) -> Self {
self.route_tag = Some(tag);
self
}
pub fn long_poll_timeout(mut self, d: Duration) -> Self {
self.long_poll_timeout = Some(d);
self
}
pub fn api_timeout(mut self, d: Duration) -> Self {
self.api_timeout = Some(d);
self
}
pub fn build(self) -> Result<WeixinConfig> {
let token = self
.token
.ok_or_else(|| Error::Config("token is required".into()))?;
Ok(WeixinConfig {
base_url: self.base_url.unwrap_or_else(|| DEFAULT_BASE_URL.to_owned()),
cdn_base_url: self
.cdn_base_url
.unwrap_or_else(|| DEFAULT_CDN_BASE_URL.to_owned()),
token,
route_tag: self.route_tag,
long_poll_timeout: self.long_poll_timeout.unwrap_or(DEFAULT_LONG_POLL_TIMEOUT),
api_timeout: self.api_timeout.unwrap_or(DEFAULT_API_TIMEOUT),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn builder_defaults() {
let cfg = WeixinConfig::builder().token("tok").build().unwrap();
assert_eq!(cfg.base_url, DEFAULT_BASE_URL);
assert_eq!(cfg.cdn_base_url, DEFAULT_CDN_BASE_URL);
assert_eq!(cfg.long_poll_timeout, DEFAULT_LONG_POLL_TIMEOUT);
assert_eq!(cfg.api_timeout, DEFAULT_API_TIMEOUT);
assert!(cfg.route_tag.is_none());
}
#[test]
fn missing_token_error() {
let result = WeixinConfig::builder().build();
assert!(result.is_err());
}
#[test]
fn custom_values() {
let cfg = WeixinConfig::builder()
.token("my_token")
.base_url("https://custom.example.com/")
.cdn_base_url("https://cdn.example.com")
.route_tag(42)
.long_poll_timeout(Duration::from_secs(10))
.api_timeout(Duration::from_secs(5))
.build()
.unwrap();
assert_eq!(cfg.token, "my_token");
assert_eq!(cfg.base_url, "https://custom.example.com/");
assert_eq!(cfg.cdn_base_url, "https://cdn.example.com");
assert_eq!(cfg.route_tag, Some(42));
assert_eq!(cfg.long_poll_timeout, Duration::from_secs(10));
assert_eq!(cfg.api_timeout, Duration::from_secs(5));
}
}