Skip to main content

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>;