1use serde::Deserialize;
2
3use super::auth;
4use super::escape;
5use super::ApiErrorResponse;
6use super::Filter;
7use super::JsonError;
8use super::Project;
9use super::Provider;
10
11const ACCEPT_HEADER_JSON: &str = "application/vnd.github.v3+json";
12const GITHUB_API_BASEURL: &str = match option_env!("GITHUB_API_BASEURL") {
13 Some(url) => url,
14 None => "https://api.github.com",
15};
16
17#[derive(Deserialize)]
18pub struct GithubProject {
19 pub name: String,
20 pub full_name: String,
21 pub clone_url: String,
22 pub ssh_url: String,
23 pub private: bool,
24}
25
26#[derive(Deserialize)]
27struct GithubUser {
28 #[serde(rename = "login")]
29 pub username: String,
30}
31
32impl Project for GithubProject {
33 fn name(&self) -> String {
34 self.name.clone()
35 }
36
37 fn namespace(&self) -> Option<String> {
38 if let Some((namespace, _name)) = self.full_name.rsplit_once('/') {
39 Some(namespace.to_string())
40 } else {
41 None
42 }
43 }
44
45 fn ssh_url(&self) -> String {
46 self.ssh_url.clone()
47 }
48
49 fn http_url(&self) -> String {
50 self.clone_url.clone()
51 }
52
53 fn private(&self) -> bool {
54 self.private
55 }
56}
57
58#[derive(Deserialize)]
59pub struct GithubApiErrorResponse {
60 pub message: String,
61}
62
63impl JsonError for GithubApiErrorResponse {
64 fn to_string(self) -> String {
65 self.message
66 }
67}
68
69pub struct Github {
70 filter: Filter,
71 secret_token: auth::AuthToken,
72}
73
74impl Provider for Github {
75 type Project = GithubProject;
76 type Error = GithubApiErrorResponse;
77
78 fn new(
79 filter: Filter,
80 secret_token: auth::AuthToken,
81 api_url_override: Option<String>,
82 ) -> Result<Self, String> {
83 if api_url_override.is_some() {
84 return Err("API URL overriding is not supported for Github".to_string());
85 }
86 Ok(Self {
87 filter,
88 secret_token,
89 })
90 }
91
92 fn filter(&self) -> &Filter {
93 &self.filter
94 }
95
96 fn secret_token(&self) -> &auth::AuthToken {
97 &self.secret_token
98 }
99
100 fn auth_header_key() -> &'static str {
101 "token"
102 }
103
104 fn get_user_projects(
105 &self,
106 user: &str,
107 ) -> Result<Vec<GithubProject>, ApiErrorResponse<GithubApiErrorResponse>> {
108 self.call_list(
109 &format!("{GITHUB_API_BASEURL}/users/{}/repos", escape(user)),
110 Some(ACCEPT_HEADER_JSON),
111 )
112 }
113
114 fn get_group_projects(
115 &self,
116 group: &str,
117 ) -> Result<Vec<GithubProject>, ApiErrorResponse<GithubApiErrorResponse>> {
118 self.call_list(
119 &format!("{GITHUB_API_BASEURL}/orgs/{}/repos?type=all", escape(group)),
120 Some(ACCEPT_HEADER_JSON),
121 )
122 }
123
124 fn get_accessible_projects(
125 &self,
126 ) -> Result<Vec<GithubProject>, ApiErrorResponse<GithubApiErrorResponse>> {
127 self.call_list(
128 &format!("{GITHUB_API_BASEURL}/user/repos"),
129 Some(ACCEPT_HEADER_JSON),
130 )
131 }
132
133 fn get_current_user(&self) -> Result<String, ApiErrorResponse<GithubApiErrorResponse>> {
134 Ok(super::call::<GithubUser, GithubApiErrorResponse>(
135 &format!("{GITHUB_API_BASEURL}/user"),
136 Self::auth_header_key(),
137 self.secret_token(),
138 Some(ACCEPT_HEADER_JSON),
139 )?
140 .username)
141 }
142}