#![allow(dead_code)]
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::PathBuf;
use std::time::Duration;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct Config {
pub general: GeneralConfig,
pub network: NetworkConfig,
pub cache: CacheConfig,
pub database: DatabaseConfig,
pub defaults: DefaultsConfig,
#[serde(default)]
pub profiles: HashMap<String, ProfileConfig>,
}
impl Default for Config {
fn default() -> Self {
Self {
general: GeneralConfig::default(),
network: NetworkConfig::default(),
cache: CacheConfig::default(),
database: DatabaseConfig::default(),
defaults: DefaultsConfig::default(),
profiles: HashMap::new(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct GeneralConfig {
pub default_format: String,
pub timezone: String,
pub color: bool,
pub quiet: bool,
}
impl Default for GeneralConfig {
fn default() -> Self {
Self {
default_format: "auto".to_string(),
timezone: "UTC".to_string(),
color: true,
quiet: false,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct NetworkConfig {
pub timeout_secs: u64,
pub retries: u32,
pub rate_limit_rps: f64,
pub proxy: Option<String>,
pub user_agent: String,
}
impl Default for NetworkConfig {
fn default() -> Self {
Self {
timeout_secs: 30,
retries: 3,
rate_limit_rps: 5.0,
proxy: None,
user_agent: format!("gdelt-cli/{}", env!("CARGO_PKG_VERSION")),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct CacheConfig {
pub enabled: bool,
pub path: Option<PathBuf>,
pub max_size_mb: u64,
pub ttl: CacheTtlConfig,
}
impl Default for CacheConfig {
fn default() -> Self {
Self {
enabled: true,
path: None,
max_size_mb: 500,
ttl: CacheTtlConfig::default(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct CacheTtlConfig {
#[serde(with = "humantime_serde")]
pub doc_search: Duration,
#[serde(with = "humantime_serde")]
pub doc_timeline: Duration,
#[serde(with = "humantime_serde")]
pub geo: Duration,
#[serde(with = "humantime_serde")]
pub tv: Duration,
#[serde(with = "humantime_serde")]
pub raw_response: Duration,
#[serde(with = "humantime_serde")]
pub grace_period: Duration,
}
impl Default for CacheTtlConfig {
fn default() -> Self {
Self {
doc_search: Duration::from_secs(3600), doc_timeline: Duration::from_secs(1800), geo: Duration::from_secs(86400), tv: Duration::from_secs(7200), raw_response: Duration::from_secs(900), grace_period: Duration::from_secs(300), }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct DatabaseConfig {
pub path: Option<PathBuf>,
pub memory_limit: String,
pub threads: Option<u32>,
pub read_only: bool,
}
impl Default for DatabaseConfig {
fn default() -> Self {
Self {
path: None,
memory_limit: "2GB".to_string(),
threads: None,
read_only: false,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct DefaultsConfig {
pub doc: DocDefaults,
pub geo: GeoDefaults,
pub tv: TvDefaults,
pub analytics: AnalyticsDefaults,
}
impl Default for DefaultsConfig {
fn default() -> Self {
Self {
doc: DocDefaults::default(),
geo: GeoDefaults::default(),
tv: TvDefaults::default(),
analytics: AnalyticsDefaults::default(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct DocDefaults {
pub timespan: String,
pub max_records: u32,
pub sort: String,
}
impl Default for DocDefaults {
fn default() -> Self {
Self {
timespan: "24h".to_string(),
max_records: 75,
sort: "hybrid-rel".to_string(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct GeoDefaults {
pub timespan: String,
pub max_points: u32,
}
impl Default for GeoDefaults {
fn default() -> Self {
Self {
timespan: "24h".to_string(),
max_points: 250,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct TvDefaults {
pub timespan: String,
pub max_records: u32,
}
impl Default for TvDefaults {
fn default() -> Self {
Self {
timespan: "7d".to_string(),
max_records: 250,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct AnalyticsDefaults {
pub timespan: String,
pub granularity: String,
pub min_entity_count: u32,
}
impl Default for AnalyticsDefaults {
fn default() -> Self {
Self {
timespan: "30d".to_string(),
granularity: "day".to_string(),
min_entity_count: 5,
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ProfileConfig {
pub cache: Option<CacheConfig>,
pub network: Option<NetworkConfig>,
pub defaults: Option<DefaultsConfig>,
}
impl Config {
pub fn with_profile(mut self, name: &str) -> Self {
if let Some(profile) = self.profiles.get(name).cloned() {
if let Some(cache) = profile.cache {
self.cache = cache;
}
if let Some(network) = profile.network {
self.network = network;
}
if let Some(defaults) = profile.defaults {
self.defaults = defaults;
}
}
self
}
pub fn to_toml(&self) -> Result<String, toml::ser::Error> {
toml::to_string_pretty(self)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = Config::default();
assert!(config.cache.enabled);
assert_eq!(config.network.timeout_secs, 30);
}
#[test]
fn test_config_serialization() {
let config = Config::default();
let toml = config.to_toml().unwrap();
assert!(toml.contains("[general]"));
assert!(toml.contains("[network]"));
}
}