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