rdap_config/config.rs
1//! Typed configuration structs for RDAPify.
2//!
3//! All structs implement [`Default`] so RDAPify runs without any config file.
4//! [`serde::Deserialize`] drives TOML parsing; absent keys use struct defaults.
5
6use serde::{Deserialize, Serialize};
7
8// ── Enumerations ──────────────────────────────────────────────────────────────
9
10/// Storage backend used for RDAP response cache entries.
11#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Default)]
12#[serde(rename_all = "lowercase")]
13pub enum CacheType {
14 /// In-process hash-map (default, no external deps).
15 #[default]
16 Memory,
17 /// SQLite file-based cache.
18 Sqlite,
19 /// PostgreSQL-backed cache.
20 Postgres,
21}
22
23impl std::str::FromStr for CacheType {
24 type Err = ();
25
26 fn from_str(s: &str) -> Result<Self, Self::Err> {
27 match s.to_lowercase().as_str() {
28 "memory" => Ok(Self::Memory),
29 "sqlite" => Ok(Self::Sqlite),
30 "postgres" => Ok(Self::Postgres),
31 _ => Err(()),
32 }
33 }
34}
35
36/// Minimum log severity emitted by RDAPify.
37#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Default)]
38#[serde(rename_all = "lowercase")]
39pub enum LogLevel {
40 Trace,
41 Debug,
42 #[default]
43 Info,
44 Warn,
45 Error,
46}
47
48impl std::str::FromStr for LogLevel {
49 type Err = ();
50
51 fn from_str(s: &str) -> Result<Self, Self::Err> {
52 match s.to_lowercase().as_str() {
53 "trace" => Ok(Self::Trace),
54 "debug" => Ok(Self::Debug),
55 "info" => Ok(Self::Info),
56 "warn" => Ok(Self::Warn),
57 "error" => Ok(Self::Error),
58 _ => Err(()),
59 }
60 }
61}
62
63/// Structured log output format.
64#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Default)]
65#[serde(rename_all = "lowercase")]
66pub enum LogFormat {
67 /// JSON (machine-readable, default for production).
68 #[default]
69 Json,
70 /// Human-readable plain text.
71 Text,
72}
73
74impl std::str::FromStr for LogFormat {
75 type Err = ();
76
77 fn from_str(s: &str) -> Result<Self, Self::Err> {
78 match s.to_lowercase().as_str() {
79 "json" => Ok(Self::Json),
80 "text" => Ok(Self::Text),
81 _ => Err(()),
82 }
83 }
84}
85
86// ── Section structs ───────────────────────────────────────────────────────────
87
88/// RDAP HTTP client configuration.
89///
90/// ```toml
91/// [rdap]
92/// timeout_seconds = 15
93/// max_response_size_mb = 5
94/// user_agent = "RDAPify/0.x"
95/// bootstrap_refresh_hours = 24
96/// ```
97#[derive(Debug, Clone, Deserialize, Serialize)]
98#[serde(default)]
99pub struct RdapConfig {
100 /// Request timeout in seconds. Range: 1–60.
101 pub timeout_seconds: u64,
102 /// Maximum allowed RDAP response size in MiB. Range: 1–20.
103 pub max_response_size_mb: u64,
104 /// `User-Agent` header sent with every RDAP request.
105 pub user_agent: String,
106 /// How often (hours) to refresh the IANA bootstrap registry.
107 pub bootstrap_refresh_hours: u64,
108}
109
110impl Default for RdapConfig {
111 fn default() -> Self {
112 Self {
113 timeout_seconds: 15,
114 max_response_size_mb: 5,
115 user_agent: "RDAPify/0.x".to_string(),
116 bootstrap_refresh_hours: 24,
117 }
118 }
119}
120
121/// Response cache configuration.
122///
123/// ```toml
124/// [cache]
125/// type = "memory"
126/// ttl_seconds = 3600
127/// max_entries = 100000
128/// ```
129#[derive(Debug, Clone, Deserialize, Serialize)]
130#[serde(default)]
131pub struct CacheConfig {
132 /// Storage backend for cached entries.
133 #[serde(rename = "type")]
134 pub backend: CacheType,
135 /// Cache entry TTL in seconds. Range: 60–86400.
136 pub ttl_seconds: u64,
137 /// Maximum number of entries to keep in the cache.
138 pub max_entries: u64,
139}
140
141impl Default for CacheConfig {
142 fn default() -> Self {
143 Self {
144 backend: CacheType::Memory,
145 ttl_seconds: 3600,
146 max_entries: 100_000,
147 }
148 }
149}
150
151/// SQLite storage backend configuration.
152///
153/// ```toml
154/// [sqlite]
155/// path = "~/.rdapify/cache.db"
156/// ```
157#[derive(Debug, Clone, Deserialize, Serialize)]
158#[serde(default)]
159pub struct SqliteConfig {
160 /// Path to the SQLite database file. `~` is expanded to `$HOME`.
161 pub path: String,
162}
163
164impl Default for SqliteConfig {
165 fn default() -> Self {
166 Self {
167 path: "~/.rdapify/cache.db".to_string(),
168 }
169 }
170}
171
172/// Background monitoring worker configuration (Pro).
173///
174/// ```toml
175/// [monitoring]
176/// workers = 4
177/// interval_seconds = 300
178/// batch_size = 50
179/// ```
180#[derive(Debug, Clone, Deserialize, Serialize)]
181#[serde(default)]
182pub struct MonitoringConfig {
183 /// Number of background monitoring workers. Range: 1–32.
184 pub workers: u32,
185 /// How often (seconds) to run a monitoring sweep.
186 pub interval_seconds: u64,
187 /// Number of domains processed per monitoring batch.
188 pub batch_size: u32,
189}
190
191impl Default for MonitoringConfig {
192 fn default() -> Self {
193 Self {
194 workers: 4,
195 interval_seconds: 300,
196 batch_size: 50,
197 }
198 }
199}
200
201/// Webhook delivery configuration (Pro).
202///
203/// ```toml
204/// [webhooks]
205/// workers = 2
206/// timeout_seconds = 10
207/// max_retries = 7
208/// ```
209#[derive(Debug, Clone, Deserialize, Serialize)]
210#[serde(default)]
211pub struct WebhookConfig {
212 /// Number of webhook delivery workers. Range: 1–32.
213 pub workers: u32,
214 /// Per-delivery HTTP request timeout in seconds. Range: 1–60.
215 pub timeout_seconds: u64,
216 /// Maximum delivery retry attempts.
217 pub max_retries: u32,
218}
219
220impl Default for WebhookConfig {
221 fn default() -> Self {
222 Self {
223 workers: 2,
224 timeout_seconds: 10,
225 max_retries: 7,
226 }
227 }
228}
229
230/// License validation configuration (Pro).
231///
232/// ```toml
233/// [license]
234/// path = "~/.rdapify/activation.rdap"
235/// grace_days = 7
236/// revocation_check_hours = 24
237/// ```
238#[derive(Debug, Clone, Deserialize, Serialize)]
239#[serde(default)]
240pub struct LicenseConfig {
241 /// Path to the offline activation record. `~` is expanded to `$HOME`.
242 pub path: String,
243 /// Days the activation cache remains valid without re-checking. Range: 0–30.
244 pub grace_days: u32,
245 /// How often (hours) to check for license revocation.
246 pub revocation_check_hours: u64,
247}
248
249impl Default for LicenseConfig {
250 fn default() -> Self {
251 Self {
252 path: "~/.rdapify/activation.rdap".to_string(),
253 grace_days: 7,
254 revocation_check_hours: 24,
255 }
256 }
257}
258
259/// Structured logging configuration.
260///
261/// ```toml
262/// [logging]
263/// level = "info"
264/// format = "json"
265/// ```
266#[derive(Debug, Clone, Deserialize, Serialize)]
267#[serde(default)]
268pub struct LoggingConfig {
269 /// Minimum log severity to emit.
270 pub level: LogLevel,
271 /// Log line output format.
272 pub format: LogFormat,
273}
274
275impl Default for LoggingConfig {
276 fn default() -> Self {
277 Self {
278 level: LogLevel::Info,
279 format: LogFormat::Json,
280 }
281 }
282}
283
284/// Prometheus-compatible metrics endpoint configuration.
285///
286/// ```toml
287/// [metrics]
288/// enabled = true
289/// port = 9090
290/// ```
291#[derive(Debug, Clone, Deserialize, Serialize)]
292#[serde(default)]
293pub struct MetricsConfig {
294 /// Expose the `/metrics` endpoint.
295 pub enabled: bool,
296 /// Port to bind the metrics HTTP server. Range: 1–65535.
297 pub port: u16,
298}
299
300impl Default for MetricsConfig {
301 fn default() -> Self {
302 Self {
303 enabled: true,
304 port: 9090,
305 }
306 }
307}
308
309/// HTTP service binding configuration (service mode).
310///
311/// ```toml
312/// [server]
313/// host = "0.0.0.0"
314/// port = 8080
315/// ```
316#[derive(Debug, Clone, Deserialize, Serialize)]
317#[serde(default)]
318pub struct ServerConfig {
319 /// Interface address to bind (e.g. `"0.0.0.0"` or `"127.0.0.1"`).
320 pub host: String,
321 /// TCP port to listen on. Range: 1–65535.
322 pub port: u16,
323}
324
325impl Default for ServerConfig {
326 fn default() -> Self {
327 Self {
328 host: "0.0.0.0".to_string(),
329 port: 8080,
330 }
331 }
332}
333
334// ── Root config ───────────────────────────────────────────────────────────────
335
336/// Complete RDAPify configuration.
337///
338/// All sections have safe defaults so RDAPify runs without any config file.
339///
340/// # Minimal example
341///
342/// ```toml
343/// [cache]
344/// type = "memory"
345///
346/// [logging]
347/// level = "info"
348/// ```
349///
350/// Everything else uses defaults.
351#[derive(Debug, Clone, Deserialize, Serialize, Default)]
352#[serde(default)]
353pub struct RdapifyConfig {
354 /// RDAP HTTP client settings.
355 pub rdap: RdapConfig,
356 /// Response cache settings.
357 pub cache: CacheConfig,
358 /// SQLite storage settings.
359 pub sqlite: SqliteConfig,
360 /// Background monitoring settings (Pro).
361 pub monitoring: MonitoringConfig,
362 /// Webhook delivery settings (Pro).
363 pub webhooks: WebhookConfig,
364 /// License validation settings (Pro).
365 pub license: LicenseConfig,
366 /// Structured logging settings.
367 pub logging: LoggingConfig,
368 /// Metrics endpoint settings.
369 pub metrics: MetricsConfig,
370 /// HTTP service binding settings (service mode).
371 pub server: ServerConfig,
372}