use std::{ collections::{BTreeMap, HashMap}, sync::{Arc, Mutex as StdMutex, OnceLock}, }; use lru_cache::LruCache; use tokio::sync::{broadcast, Mutex, RwLock}; use crate::{observability::FilterReloadHandles, Config, Result}; pub(crate) mod account_data; pub(crate) mod admin; pub(crate) mod appservice; pub(crate) mod globals; pub(crate) mod key_backups; pub(crate) mod media; pub(crate) mod pdu; pub(crate) mod pusher; pub(crate) mod rooms; pub(crate) mod sending; pub(crate) mod transaction_ids; pub(crate) mod uiaa; pub(crate) mod users; static SERVICES: OnceLock<&'static Services> = OnceLock::new(); /// Convenient access to the global [`Services`] instance pub(crate) fn services() -> &'static Services { SERVICES.get().expect("`Services::install` should have been called first") } pub(crate) struct Services { pub(crate) appservice: appservice::Service, pub(crate) pusher: pusher::Service, pub(crate) rooms: rooms::Service, pub(crate) transaction_ids: transaction_ids::Service, pub(crate) uiaa: uiaa::Service, pub(crate) users: users::Service, pub(crate) account_data: account_data::Service, pub(crate) admin: Arc, pub(crate) globals: globals::Service, pub(crate) key_backups: key_backups::Service, pub(crate) media: media::Service, pub(crate) sending: Arc, } impl Services { #[allow(clippy::too_many_lines)] pub(crate) fn build< D: appservice::Data + pusher::Data + rooms::Data + transaction_ids::Data + uiaa::Data + users::Data + account_data::Data + globals::Data + key_backups::Data + media::Data + sending::Data + 'static, >( db: &'static D, config: Config, reload_handles: FilterReloadHandles, ) -> Result { Ok(Self { appservice: appservice::Service::build(db)?, pusher: pusher::Service { db, }, rooms: rooms::Service { alias: rooms::alias::Service::new(db), auth_chain: rooms::auth_chain::Service::new( db, #[allow( clippy::as_conversions, clippy::cast_sign_loss, clippy::cast_possible_truncation )] { (100_000.0 * config.cache_capacity_modifier) as usize }, ), directory: db, edus: rooms::edus::Service { read_receipt: db, typing: rooms::edus::typing::Service { typing: RwLock::new(BTreeMap::new()), last_typing_update: RwLock::new(BTreeMap::new()), typing_update_sender: broadcast::channel(100).0, }, }, event_handler: rooms::event_handler::Service, lazy_loading: rooms::lazy_loading::Service { db, lazy_load_waiting: Mutex::new(HashMap::new()), }, metadata: db, outlier: db, pdu_metadata: rooms::pdu_metadata::Service { db, }, search: db, short: rooms::short::Service::new( db, #[allow( clippy::as_conversions, clippy::cast_sign_loss, clippy::cast_possible_truncation )] { (100_000.0 * config.cache_capacity_modifier) as usize }, #[allow( clippy::as_conversions, clippy::cast_sign_loss, clippy::cast_possible_truncation )] { (100_000.0 * config.cache_capacity_modifier) as usize }, ), state: rooms::state::Service { db, }, state_accessor: rooms::state_accessor::Service { db, #[allow( clippy::as_conversions, clippy::cast_sign_loss, clippy::cast_possible_truncation )] server_visibility_cache: StdMutex::new(LruCache::new( (100.0 * config.cache_capacity_modifier) as usize, )), #[allow( clippy::as_conversions, clippy::cast_sign_loss, clippy::cast_possible_truncation )] user_visibility_cache: StdMutex::new(LruCache::new( (100.0 * config.cache_capacity_modifier) as usize, )), }, state_cache: rooms::state_cache::Service { db, }, state_compressor: rooms::state_compressor::Service { db, #[allow( clippy::as_conversions, clippy::cast_sign_loss, clippy::cast_possible_truncation )] stateinfo_cache: StdMutex::new(LruCache::new( (100.0 * config.cache_capacity_modifier) as usize, )), }, timeline: rooms::timeline::Service::new( db, config.pdu_cache_capacity, ), threads: rooms::threads::Service { db, }, spaces: rooms::spaces::Service { roomid_spacechunk_cache: Mutex::new(LruCache::new(200)), }, user: db, }, transaction_ids: db, uiaa: uiaa::Service::new(db), users: users::Service { db, connections: StdMutex::new(BTreeMap::new()), }, account_data: db, admin: admin::Service::build(), key_backups: db, media: media::Service { db, }, sending: sending::Service::build(db, &config), globals: globals::Service::load(db, config, reload_handles)?, }) } /// Installs `self` to be globally accessed via [`services`] pub(crate) fn install(self) { assert!( SERVICES.set(Box::leak(Box::new(self))).is_ok(), "Services::install was called more than once" ); } }