1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
use std::sync::{Arc, Mutex};

use ruma_client_api::discovery::get_supported_versions;
use ruma_common::api::{MatrixVersion, SendAccessToken};

use super::{Client, ClientData};
use crate::{DefaultConstructibleHttpClient, Error, HttpClient, HttpClientExt};

/// A [`Client`] builder.
///
/// This type can be used to construct a `Client` through a few method calls.
pub struct ClientBuilder {
    homeserver_url: Option<String>,
    access_token: Option<String>,
    supported_matrix_versions: Option<Vec<MatrixVersion>>,
}

impl ClientBuilder {
    pub(super) fn new() -> Self {
        Self { homeserver_url: None, access_token: None, supported_matrix_versions: None }
    }

    /// Set the homeserver URL.
    ///
    /// The homeserver URL must be set before calling [`build()`][Self::build] or
    /// [`http_client()`][Self::http_client].
    pub fn homeserver_url(self, url: String) -> Self {
        Self { homeserver_url: Some(url), ..self }
    }

    /// Set the access token.
    pub fn access_token(self, access_token: Option<String>) -> Self {
        Self { access_token, ..self }
    }

    /// Set the supported Matrix versions.
    ///
    /// This method generally *shouldn't* be called. The [`build()`][Self::build] or
    /// [`http_client()`][Self::http_client] method will take care of doing a
    /// [`get_supported_versions`] request to find out about the supported versions.
    pub fn supported_matrix_versions(self, versions: Vec<MatrixVersion>) -> Self {
        Self { supported_matrix_versions: Some(versions), ..self }
    }

    /// Finish building the [`Client`].
    ///
    /// Uses [`DefaultConstructibleHttpClient::default()`] to create an HTTP client instance.
    /// Unless the supported Matrix versions were manually set via
    /// [`supported_matrix_versions`][Self::supported_matrix_versions], this will do a
    /// [`get_supported_versions`] request to find out about the supported versions.
    pub async fn build<C>(self) -> Result<Client<C>, Error<C::Error, ruma_client_api::Error>>
    where
        C: DefaultConstructibleHttpClient,
    {
        self.http_client(C::default()).await
    }

    /// Set the HTTP client to finish building the [`Client`].
    ///
    /// Unless the supported Matrix versions were manually set via
    /// [`supported_matrix_versions`][Self::supported_matrix_versions], this will do a
    /// [`get_supported_versions`] request to find out about the supported versions.
    pub async fn http_client<C>(
        self,
        http_client: C,
    ) -> Result<Client<C>, Error<C::Error, ruma_client_api::Error>>
    where
        C: HttpClient,
    {
        let homeserver_url = self
            .homeserver_url
            .expect("homeserver URL has to be set prior to calling .build() or .http_client()");

        let supported_matrix_versions = match self.supported_matrix_versions {
            Some(versions) => versions,
            None => http_client
                .send_matrix_request(
                    &homeserver_url,
                    SendAccessToken::None,
                    &[MatrixVersion::V1_0],
                    get_supported_versions::Request::new(),
                )
                .await?
                .known_versions()
                .collect(),
        };

        Ok(Client(Arc::new(ClientData {
            homeserver_url,
            http_client,
            access_token: Mutex::new(self.access_token),
            supported_matrix_versions,
        })))
    }
}