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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
//! Contains a builder for [`Client`].

use std::{ffi::OsStr, fs::File, io::prelude::*, path::Path};

use anyhow::{Error, Result};

use crate::github::{handler::EventHandler, util::*, Client, DefaultEventHandler};

/// A builder for [`Client`]
pub struct ClientBuilder<T>
where
    T: std::fmt::Debug + EventHandler<GitHubClient = Client<T>> + Send + Sync + 'static,
{
    handler: Option<T>,
    auth: Option<Authorization>,
    user_agent: Option<String>,
    #[cfg(feature = "native")]
    payload_size: Option<u64>,
}

impl<T> ClientBuilder<T>
where
    T: std::fmt::Debug + EventHandler<GitHubClient = Client<T>> + Send + Sync + 'static,
{
    /// Creates a new [`ClientBuilder`]
    pub fn new() -> Self {
        Self::default()
    }

    /// Adds an [`EventHandler`] to the current builder.
    pub fn event_handler(mut self, event_handler: T) -> Self {
        self.handler = Some(event_handler);
        self
    }

    /// Sets the maximum payload size that the listener can receive from GitHub
    /// in MiB. Default: 8.
    #[cfg(feature = "native")]
    pub fn payload_size(mut self, size: u64) -> Self {
        self.payload_size = Some(size);
        self
    }

    /// Sets a custom user agent for your application. Default is "Octocat-rs".
    ///
    /// See also: [`HttpClient::set_ua`]
    ///
    /// [`HttpClient::set_ua`]: crate::github::HttpClient::set_ua
    pub fn user_agent<V: Into<String>>(mut self, user_agent: V) -> Self {
        self.user_agent = Some(user_agent.into());
        self
    }

    /// Adds an [`Authorization`] instance to the current builder using input
    /// from a file.
    pub fn credentials_file<P: AsRef<Path>>(self, file: P) -> Self {
        let mut f = File::open(file).expect("ClientBuilder: Opening authorization file");
        let mut contents = "".to_owned();

        f.read_to_string(&mut contents)
            .expect("ClientBuilder: Reading authorization file");

        let auth: Authorization = toml::from_str::<OctocatConfig>(contents.as_str())
            .expect("ClientBuilder: Parsing authorization file")
            .to_personal_auth();

        self.set_auth(Some(auth))
    }

    /// Adds an [`Authorization`] instance to the current builder using input
    /// from an environment variable.
    pub fn credentials_env_var<K: AsRef<OsStr>>(self, username_var: K, token_var: K) -> Self {
        let auth = {
            let username = std::env::var(username_var).expect("username not set!");

            let token = std::env::var(token_var).expect("token not set!");

            Authorization::PersonalToken { username, token }
        };

        self.set_auth(Some(auth))
    }

    /// Adds an [`Authorization`] instance to the current builder.
    pub fn personal_auth<V: Into<String>>(self, username: V, token: V) -> Self {
        let auth = Authorization::PersonalToken {
            username: username.into(),
            token: token.into(),
        };

        self.set_auth(Some(auth))
    }

    fn set_auth(mut self, auth: Option<Authorization>) -> Self {
        self.auth = auth;
        self
    }

    /// Builds the current builder. In other words, this turns a
    /// [`ClientBuilder`] into a [`Client`]. Requires a handler to be set.
    pub fn build(self) -> Result<Client<T>> {
        if self.handler.is_none() {
            return Err(Error::from(BuildError::NoHandler));
        }

        Ok(Client::new(
            self.handler.unwrap(),
            self.auth,
            self.user_agent,
            #[cfg(feature = "native")]
            self.payload_size,
        ))
    }
}

impl ClientBuilder<DefaultEventHandler> {
    /// Returns the default implementation of [`Client`]
    pub fn build_unconfigured() -> Client<DefaultEventHandler> {
        Client::default()
    }

    /// For building the current builder without setting a handler.
    ///
    /// Requires T to be set to [`DefaultEventHandler`].
    pub fn build_no_handler(self) -> Result<Client<DefaultEventHandler>> {
        Ok(Client::new(
            DefaultEventHandler::new(),
            self.auth,
            self.user_agent,
            #[cfg(feature = "native")]
            self.payload_size,
        ))
    }
}

impl<T> Default for ClientBuilder<T>
where
    T: std::fmt::Debug + EventHandler<GitHubClient = Client<T>> + Send + Sync + 'static,
{
    fn default() -> Self {
        Self {
            handler: None,
            auth: None,
            #[cfg(feature = "native")]
            payload_size: None,
            user_agent: None,
        }
    }
}