use figment::providers::{Env, Format as _, Toml}; use figment::Figment; use serde::Deserialize; pub fn load_config() -> eyre::Result { let conf = Figment::new() .merge(Toml::file("Config.toml")) .merge(Env::prefixed("PKC_").split("__")) .extract()?; Ok(conf) } #[derive(Debug, Deserialize)] pub struct Config { pub index_uri: String, pub database: deadpool_postgres::Config, pub plc_directory: Option, /// Adds contact details (email / bluesky handle / website) to the UA header. pub ua_contact: Option, /// Time-based retention window in days /// /// When set, only process and store records from the last N days. /// Records older than this (based on TID timestamps) will be filtered out. /// /// This applies to: /// - Posts (app.bsky.feed.post) /// - Likes (app.bsky.feed.like) /// - Reposts (app.bsky.feed.repost) /// - References to old posts (reply parents, quote embeds, like/repost subjects) /// /// Other record types (Profiles, Lists, Follows, Blocks) are always processed. /// /// Default: None (no retention limit, process all records) pub retention_days: Option, /// Configuration items specific to indexer pub indexer: Option, } #[derive(Debug, Deserialize, Clone)] pub struct IndexerConfig { /// Tap WebSocket URL (e.g., "ws://localhost:2480/channel") pub tap_websocket_url: String, /// Tap admin API URL (e.g., "http://localhost:2480") pub tap_admin_url: String, /// Tap admin password (if authentication is required) pub tap_admin_password: Option, /// Number of worker threads for processing events #[serde(default = "default_indexer_workers")] pub workers: u8, /// Maximum number of unacknowledged events to buffer #[serde(default = "default_max_pending_acks")] pub max_pending_acks: usize, } const fn default_indexer_workers() -> u8 { 4 } const fn default_max_pending_acks() -> usize { 1000 }