1use crate::console::{Console, Error};
2use crate::ensure_newline;
3use crate::subscription::{BoxedSubscription, Subscription};
4use std::collections::hash_map::Entry;
5use std::collections::HashMap;
6use std::fmt::Debug;
7use std::hash::Hash;
8use tokio::net::ToSocketAddrs;
9
10pub struct Builder<Services, A> {
12 subscriptions: HashMap<Services, BoxedSubscription>,
13 bind_address: Option<A>,
14 welcome: Option<String>,
15 accept_only_localhost: bool,
16}
17
18impl<Services, A> Builder<Services, A>
19where
20 Services: Eq + Hash + Debug,
21 A: ToSocketAddrs,
22{
23 pub fn new() -> Self {
24 Self {
25 subscriptions: HashMap::new(),
26 bind_address: None,
27 welcome: None,
28 accept_only_localhost: false,
29 }
30 }
31
32 pub fn subscribe<S>(mut self, service_id: Services, subscription: S) -> Result<Self, Error>
33 where
34 S: Subscription + Send + Sync + 'static,
35 {
36 let service_id_string = format!("{service_id:?}");
38
39 match self.subscriptions.entry(service_id) {
40 Entry::Occupied(_) => Err(Error::ServiceIdUsed(service_id_string)),
41 Entry::Vacant(entry) => {
42 entry.insert(Box::new(subscription));
43 Ok(self)
44 }
45 }
46 }
47
48 pub fn bind_address(mut self, bind_address: A) -> Self {
49 self.bind_address = Some(bind_address);
50 self
51 }
52
53 pub fn welcome(mut self, message: &str) -> Self {
54 self.welcome = Some(message.to_owned());
55 self
56 }
57
58 pub fn accept_only_localhost(mut self) -> Self {
59 self.accept_only_localhost = true;
60 self
61 }
62
63 pub fn build(self) -> Result<Console<Services, A>, Error> {
64 let Some(bind_address) = self.bind_address else {
65 return Err(Error::NoBindAddress);
66 };
67
68 Ok(Console::new(
69 self.subscriptions,
70 bind_address,
71 ensure_newline(self.welcome.unwrap_or_default()),
72 self.accept_only_localhost,
73 ))
74 }
75}
76
77impl<Services, A> Default for Builder<Services, A>
78where
79 Services: Eq + Hash + Debug,
80 A: ToSocketAddrs,
81{
82 fn default() -> Self {
83 Self::new()
84 }
85}