use components::EphemeralRegistry; use crossbeam_channel::Receiver; use libchat::{ChatError, ChatStorage, IdentityProvider, RegistrationService, StorageConfig}; use storage::ChatStore; use crate::Transport; use crate::client::ChatClient; use crate::delegate::DelegateSigner; use crate::errors::ClientError; use crate::event::Event; /// Marker for a builder field that has not been configured. A field left `Unset` /// at `build()` is a compile error: `build()` requires every component to have a /// concrete type. Use [`ChatClientBuilder::default`] to start from filled-in /// defaults instead. pub struct Unset; pub struct ChatClientBuilder { ident: I, transport: T, registration: R, storage: S, } impl ChatClientBuilder { /// An empty builder: every component is `Unset` and must be supplied before /// [`build`](ChatClientBuilder::build). For the common case, prefer /// [`default`](ChatClientBuilder::default), which pre-fills the components. // // `Default` is intentionally not implemented: `default()` below is a distinct // constructor that returns a *different*, pre-filled builder type, which the // `Default` trait (`fn() -> Self`) cannot express. #[allow(clippy::new_without_default)] pub fn new() -> Self { Self { ident: Unset, transport: Unset, registration: Unset, storage: Unset, } } /// A builder pre-filled with the default identity, registration, and storage. /// Only the transport is left to set; override any default with the matching /// setter. A complete entry point on its own — there is no need to call /// [`new`](ChatClientBuilder::new) first. #[allow(clippy::should_implement_trait)] pub fn default() -> ChatClientBuilder { Self::new() .ident(DelegateSigner::random()) .registration(EphemeralRegistry::new()) .storage(ChatStorage::in_memory()) } } impl ChatClientBuilder { pub fn ident(self, ident: NI) -> ChatClientBuilder { ChatClientBuilder { ident, transport: self.transport, registration: self.registration, storage: self.storage, } } pub fn transport(self, transport: NT) -> ChatClientBuilder { ChatClientBuilder { ident: self.ident, transport, registration: self.registration, storage: self.storage, } } pub fn registration(self, registration: NR) -> ChatClientBuilder { ChatClientBuilder { ident: self.ident, transport: self.transport, registration, storage: self.storage, } } pub fn storage(self, storage: NS) -> ChatClientBuilder { ChatClientBuilder { ident: self.ident, transport: self.transport, registration: self.registration, storage, } } pub fn storage_config(self, config: StorageConfig) -> ChatClientBuilder { let storage = ChatStorage::new(config) .map_err(ChatError::from) .expect("Storage config file should be valid"); ChatClientBuilder { ident: self.ident, transport: self.transport, registration: self.registration, storage, } } } type Built = Result<(ChatClient, Receiver), ClientError>; /// `build()` exists only once every component has a concrete type. Any field /// still `Unset` (always at least the transport, which has no default) fails the /// bounds below, so an incomplete builder is a compile error rather than a /// runtime one. Start from [`default`](ChatClientBuilder::default) to fill the /// identity/registration/storage slots, then set the transport. impl ChatClientBuilder where I: IdentityProvider + Send + 'static, T: Transport + Send + 'static, R: RegistrationService + Send + 'static, S: ChatStore + Send + 'static, { pub fn build(self) -> Built { ChatClient::new(self.ident, self.transport, self.registration, self.storage) } }