translation_lib/
config.rs1use crate::error::{TranslationError, TranslationResult};
6use serde::{Deserialize, Serialize};
7use std::time::Duration;
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct TranslationConfig {
29 pub api_url: String,
31
32 pub api_key: Option<String>,
34
35 #[serde(with = "serde_duration")]
37 pub timeout: Duration,
38
39 pub max_concurrent_requests: usize,
41
42 pub enable_cache: bool,
44}
45
46impl Default for TranslationConfig {
47 fn default() -> Self {
48 Self {
49 api_url: "https://api.example.com/translate".to_string(),
50 api_key: None,
51 timeout: Duration::from_secs(30),
52 max_concurrent_requests: 10,
53 enable_cache: true,
54 }
55 }
56}
57
58impl TranslationConfig {
59 pub fn new(api_url: String) -> Self {
61 Self {
62 api_url,
63 ..Default::default()
64 }
65 }
66
67 pub fn with_api_key(mut self, api_key: String) -> Self {
69 self.api_key = Some(api_key);
70 self
71 }
72
73 pub fn with_timeout(mut self, timeout: Duration) -> Self {
75 self.timeout = timeout;
76 self
77 }
78
79 pub fn with_max_concurrent(mut self, max_concurrent: usize) -> Self {
81 self.max_concurrent_requests = max_concurrent;
82 self
83 }
84
85 pub fn with_cache(mut self, enable_cache: bool) -> Self {
87 self.enable_cache = enable_cache;
88 self
89 }
90
91 pub fn from_env() -> TranslationResult<Self> {
93 let api_url = std::env::var("TRANSLATION_API_URL")
94 .unwrap_or_else(|_| "https://api.example.com/translate".to_string());
95
96 let api_key = std::env::var("TRANSLATION_API_KEY").ok();
97
98 let timeout_secs = std::env::var("TRANSLATION_TIMEOUT")
99 .unwrap_or_else(|_| "30".to_string())
100 .parse::<u64>()
101 .map_err(|_| TranslationError::Config("Invalid timeout value".to_string()))?;
102
103 let max_concurrent = std::env::var("TRANSLATION_MAX_CONCURRENT")
104 .unwrap_or_else(|_| "10".to_string())
105 .parse::<usize>()
106 .map_err(|_| TranslationError::Config("Invalid max_concurrent value".to_string()))?;
107
108 let enable_cache = std::env::var("TRANSLATION_ENABLE_CACHE")
109 .unwrap_or_else(|_| "true".to_string())
110 .parse::<bool>()
111 .map_err(|_| TranslationError::Config("Invalid enable_cache value".to_string()))?;
112
113 Ok(Self {
114 api_url,
115 api_key,
116 timeout: Duration::from_secs(timeout_secs),
117 max_concurrent_requests: max_concurrent,
118 enable_cache,
119 })
120 }
121
122 pub fn validate(&self) -> TranslationResult<()> {
124 if self.api_url.is_empty() {
125 return Err(TranslationError::Config(
126 "API URL cannot be empty".to_string(),
127 ));
128 }
129
130 if !self.api_url.starts_with("http://") && !self.api_url.starts_with("https://") {
131 return Err(TranslationError::Config(
132 "API URL must start with http:// or https://".to_string(),
133 ));
134 }
135
136 if self.max_concurrent_requests == 0 {
137 return Err(TranslationError::Config(
138 "max_concurrent_requests must be greater than 0".to_string(),
139 ));
140 }
141
142 if self.timeout.as_secs() == 0 {
143 return Err(TranslationError::Config(
144 "timeout must be greater than 0".to_string(),
145 ));
146 }
147
148 Ok(())
149 }
150}
151
152mod serde_duration {
154 use serde::{Deserialize, Deserializer, Serialize, Serializer};
155 use std::time::Duration;
156
157 pub fn serialize<S>(duration: &Duration, serializer: S) -> Result<S::Ok, S::Error>
158 where
159 S: Serializer,
160 {
161 duration.as_secs().serialize(serializer)
162 }
163
164 pub fn deserialize<'de, D>(deserializer: D) -> Result<Duration, D::Error>
165 where
166 D: Deserializer<'de>,
167 {
168 let secs = u64::deserialize(deserializer)?;
169 Ok(Duration::from_secs(secs))
170 }
171}