rdap_config/lib.rs
1//! Configuration system for RDAPify.
2//!
3//! Loads [`RdapifyConfig`] from a TOML file and environment variables,
4//! applies safe defaults for missing fields, and validates all values
5//! before returning.
6//!
7//! # Quick start
8//!
9//! ```rust,no_run
10//! use rdap_config::load_config;
11//!
12//! let config = load_config(None).expect("valid config");
13//! println!("Timeout: {}s", config.rdap.timeout_seconds);
14//! println!("Log level: {:?}", config.logging.level);
15//! println!("Server: {}:{}", config.server.host, config.server.port);
16//! ```
17//!
18//! # Config file search order
19//!
20//! | Priority | Source |
21//! |----------|-------------------------------|
22//! | 1 | `--config <path>` CLI flag |
23//! | 2 | `$RDAPIFY_CONFIG` |
24//! | 3 | `./rdapify.toml` |
25//! | 4 | `~/.rdapify/rdapify.toml` |
26//! | 5 | `/etc/rdapify/rdapify.toml` |
27//!
28//! If no file is found, all-default configuration is used without error.
29//!
30//! # Environment variable overrides
31//!
32//! | Variable | Config field |
33//! |------------------------|------------------------|
34//! | `RDAPIFY_RDAP_TIMEOUT` | `rdap.timeout_seconds` |
35//! | `RDAPIFY_CACHE_TYPE` | `cache.type` |
36//! | `RDAPIFY_SQLITE_PATH` | `sqlite.path` |
37//! | `RDAPIFY_LICENSE_PATH` | `license.path` |
38//! | `RDAPIFY_LOG_LEVEL` | `logging.level` |
39//! | `RDAPIFY_LOG_FORMAT` | `logging.format` |
40//! | `RDAPIFY_METRICS_PORT` | `metrics.port` |
41//! | `RDAPIFY_SERVER_PORT` | `server.port` |
42
43#![forbid(unsafe_code)]
44
45mod config;
46mod env;
47mod loader;
48mod validate;
49
50pub use config::{
51 CacheConfig, CacheType, LicenseConfig, LogFormat, LogLevel, LoggingConfig, MetricsConfig,
52 MonitoringConfig, RdapConfig, RdapifyConfig, ServerConfig, SqliteConfig, WebhookConfig,
53};
54pub use loader::{expand_tilde, load_config};
55
56use thiserror::Error;
57
58/// Errors produced by the configuration system.
59#[derive(Debug, Error)]
60pub enum ConfigError {
61 /// An explicit config path (from `--config` or `$RDAPIFY_CONFIG`) does not
62 /// exist on disk.
63 #[error("Config file not found: {0}")]
64 FileNotFound(String),
65
66 /// A config file exists but could not be read (permissions, I/O error, etc.).
67 #[error("Failed to read config file '{path}': {source}")]
68 ReadError {
69 path: String,
70 #[source]
71 source: std::io::Error,
72 },
73
74 /// A config file exists and was read, but is not valid TOML or does not
75 /// match the expected schema.
76 #[error("Failed to parse config file '{path}': {source}")]
77 ParseError {
78 path: String,
79 #[source]
80 source: toml::de::Error,
81 },
82
83 /// A field value is outside the allowed range or fails a business rule.
84 #[error("Invalid config value for '{field}' = {value}: {reason}")]
85 ValidationError {
86 field: String,
87 value: String,
88 reason: String,
89 },
90
91 /// An environment variable override contains an unparseable value.
92 #[error("Invalid environment variable {var}: {reason}")]
93 EnvError { var: String, reason: String },
94}
95
96/// Convenience `Result` alias for this crate.
97pub type Result<T> = std::result::Result<T, ConfigError>;