use_github_api/client/
mod.rs1use crate::users::Users;
2#[cfg(feature = "enterprise")]
3use crate::CreationError;
4use reqwest::{
5 header::{HeaderMap, ACCEPT},
6 Client,
7};
8use std::error::Error as StdError;
9
10#[cfg(any(feature = "auth", doc))]
11#[cfg_attr(docsrs, doc(cfg(feature = "auth")))]
12mod builder;
13
14#[cfg(any(feature = "auth", doc))]
15pub use builder::GithubClientBuilder;
16
17pub(crate) mod macros {
18 #[macro_export]
19 #[doc(hidden)]
20 macro_rules! url {
21 ($self:expr, $i:expr) => {
22 format!("{}{}", $self.client.base_url, $i)
23 };
24 ($self:expr, $i:expr, $($arg:expr),*) => {
25 format!("{}{}", $self.client.base_url, format!($i, $($arg),*))
26 };
27 }
28}
29
30#[derive(Debug)]
31pub struct GithubClient<'a> {
33 pub(crate) base_url: &'a str,
34 pub(crate) reqwest_client: Client,
35 #[cfg(feature = "auth")]
36 auth_token: &'a str,
37 pub(crate) default_headers: HeaderMap,
38 user_agent: &'a str,
39}
40
41impl<'a> GithubClient<'a> {
42 pub fn new(
66 #[cfg(feature = "enterprise")] base_url: &'a str,
67 #[cfg(feature = "auth")] auth_token: &'a str,
68 ) -> Result<GithubClient<'a>, Box<dyn StdError>> {
69 #[cfg(feature = "enterprise")]
70 if !(base_url.starts_with("https://") || base_url.starts_with("http://")) {
71 return Err(CreationError::base_url_without_protocol().into());
72 }
73 #[cfg(feature = "enterprise")]
74 if !base_url.ends_with("/api/v3") {
75 return Err(CreationError::base_url_without_api_path().into());
76 }
77 let mut headers = HeaderMap::new();
78 headers.insert(ACCEPT, "application/vnd.github.v3+json".parse().unwrap());
79 #[cfg(feature = "auth")]
80 if let Ok(token_header) = format!("token {}", auth_token).parse() {
81 headers.insert("Authorization", token_header);
82 }
83 const UA: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"));
84 let client = Client::builder()
85 .default_headers(headers.clone())
86 .user_agent(UA)
87 .build()?;
88 Ok(Self {
89 #[cfg(feature = "enterprise")]
90 base_url,
91 #[cfg(not(feature = "enterprise"))]
92 base_url: "https://api.github.com",
93 reqwest_client: client,
94 #[cfg(feature = "auth")]
95 auth_token,
96 default_headers: headers,
97 user_agent: UA,
98 })
99 }
100
101 #[cfg(feature = "auth")]
102 pub fn builder() -> GithubClientBuilder<'a> {
104 GithubClientBuilder::new()
105 }
106
107 pub fn users(&self) -> Users<'_> {
108 Users::new(&self)
109 }
110}
111
112#[cfg(not(feature = "auth"))]
113impl<'a> Default for GithubClient<'a> {
114 fn default() -> Self {
115 Self::new().expect("Error while creating default client")
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 #[cfg(feature = "auth")]
122 use crate::constants::FAKE_TOKEN;
123
124 use super::*;
125 #[test]
126 fn new_creates_client_correctly() {
127 #[cfg(feature = "auth")]
128 use reqwest::header::HeaderValue;
129 let client = GithubClient::new(
130 #[cfg(feature = "enterprise")]
131 "https://something.com/api/v3",
132 #[cfg(feature = "auth")]
133 FAKE_TOKEN,
134 )
135 .expect("Should build client");
136 #[cfg(feature = "auth")]
137 assert_eq!(client.auth_token, FAKE_TOKEN);
138 #[cfg(feature = "enterprise")]
139 assert_eq!(client.base_url, "https://something.com/api/v3");
140 #[cfg(feature = "auth")]
141 assert_eq!(
142 client.default_headers.get("Authorization"),
143 Some(
144 &format!("token {}", FAKE_TOKEN)
145 .parse::<HeaderValue>()
146 .unwrap()
147 )
148 );
149 assert_eq!(
150 client.user_agent,
151 format!("{}/{}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"))
152 );
153 }
154
155 #[test]
156 #[cfg(feature = "auth")]
157 fn setting_auth_token_sets_header() {
158 #[cfg(not(feature = "enterprise"))]
159 let client = GithubClient::new("abc").expect("Should build client");
160 #[cfg(feature = "enterprise")]
161 let client =
162 GithubClient::new("https://abc.abc/api/v3", "abc").expect("Should build client");
163 assert_eq!(
164 client.default_headers.get("Authorization"),
165 Some(&"token abc".parse().unwrap())
166 );
167 }
168
169 #[test]
170 #[cfg(feature = "enterprise")]
171 #[should_panic(expected = "CreationError { kind: BaseUrlWithoutProtocol }")]
172 fn new_errors_on_no_protocol() {
173 GithubClient::new("something", FAKE_TOKEN).expect("Should not work");
174 }
175
176 #[test]
177 #[cfg(feature = "enterprise")]
178 #[should_panic(expected = "CreationError { kind: BaseUrlWithoutApiPath }")]
179 fn new_errors_on_no_api_path() {
180 GithubClient::new("https://something.com", FAKE_TOKEN).unwrap();
181 }
182
183 #[test]
184 #[cfg(feature = "enterprise")]
185 fn new_for_valid_enterprise_works() {
186 GithubClient::new("https://something.com/api/v3", FAKE_TOKEN).unwrap();
187 }
188}