Skip to main content

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}