git_cliff_core/remote/
github.rs1use crate::config::Remote;
2use crate::error::*;
3use reqwest_middleware::ClientWithMiddleware;
4use serde::{
5 Deserialize,
6 Serialize,
7};
8
9use super::*;
10
11pub const START_FETCHING_MSG: &str = "Retrieving data from GitHub...";
13
14pub const FINISHED_FETCHING_MSG: &str = "Done fetching GitHub data.";
16
17pub(crate) const TEMPLATE_VARIABLES: &[&str] =
19 &["github", "commit.github", "commit.remote"];
20
21#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
23pub struct GitHubCommit {
24 pub sha: String,
26 pub author: Option<GitHubCommitAuthor>,
28 pub commit: Option<GitHubCommitDetails>,
30}
31
32#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
34pub struct GitHubCommitDetails {
35 pub author: GitHubCommitDetailsAuthor,
37}
38
39#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
41pub struct GitHubCommitDetailsAuthor {
42 pub date: String,
44}
45
46impl RemoteCommit for GitHubCommit {
47 fn id(&self) -> String {
48 self.sha.clone()
49 }
50
51 fn username(&self) -> Option<String> {
52 self.author.clone().and_then(|v| v.login)
53 }
54
55 fn timestamp(&self) -> Option<i64> {
56 self.commit
57 .clone()
58 .map(|f| self.convert_to_unix_timestamp(f.author.date.clone().as_str()))
59 }
60}
61
62impl RemoteEntry for GitHubCommit {
63 fn url(_id: i64, api_url: &str, remote: &Remote, page: i32) -> String {
64 format!(
65 "{}/repos/{}/{}/commits?per_page={MAX_PAGE_SIZE}&page={page}",
66 api_url, remote.owner, remote.repo
67 )
68 }
69 fn buffer_size() -> usize {
70 10
71 }
72
73 fn early_exit(&self) -> bool {
74 false
75 }
76}
77
78#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
80pub struct GitHubCommitAuthor {
81 pub login: Option<String>,
83}
84
85#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
87#[serde(rename_all = "camelCase")]
88pub struct PullRequestLabel {
89 pub name: String,
91}
92
93#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
95pub struct GitHubPullRequest {
96 pub number: i64,
98 pub title: Option<String>,
100 pub merge_commit_sha: Option<String>,
102 pub labels: Vec<PullRequestLabel>,
104}
105
106impl RemotePullRequest for GitHubPullRequest {
107 fn number(&self) -> i64 {
108 self.number
109 }
110
111 fn title(&self) -> Option<String> {
112 self.title.clone()
113 }
114
115 fn labels(&self) -> Vec<String> {
116 self.labels.iter().map(|v| v.name.clone()).collect()
117 }
118
119 fn merge_commit(&self) -> Option<String> {
120 self.merge_commit_sha.clone()
121 }
122}
123
124impl RemoteEntry for GitHubPullRequest {
125 fn url(_id: i64, api_url: &str, remote: &Remote, page: i32) -> String {
126 format!(
127 "{}/repos/{}/{}/pulls?per_page={MAX_PAGE_SIZE}&page={page}&state=closed",
128 api_url, remote.owner, remote.repo
129 )
130 }
131
132 fn buffer_size() -> usize {
133 5
134 }
135
136 fn early_exit(&self) -> bool {
137 false
138 }
139}
140
141#[derive(Debug, Clone)]
143pub struct GitHubClient {
144 remote: Remote,
146 client: ClientWithMiddleware,
148}
149
150impl TryFrom<Remote> for GitHubClient {
152 type Error = Error;
153 fn try_from(remote: Remote) -> Result<Self> {
154 Ok(Self {
155 client: remote.create_client("application/vnd.github+json")?,
156 remote,
157 })
158 }
159}
160
161impl RemoteClient for GitHubClient {
162 const API_URL: &'static str = "https://api.github.com";
163 const API_URL_ENV: &'static str = "GITHUB_API_URL";
164
165 fn remote(&self) -> Remote {
166 self.remote.clone()
167 }
168
169 fn client(&self) -> ClientWithMiddleware {
170 self.client.clone()
171 }
172}
173
174impl GitHubClient {
175 pub async fn get_commits(&self) -> Result<Vec<Box<dyn RemoteCommit>>> {
177 Ok(self
178 .fetch::<GitHubCommit>(0)
179 .await?
180 .into_iter()
181 .map(|v| Box::new(v) as Box<dyn RemoteCommit>)
182 .collect())
183 }
184
185 pub async fn get_pull_requests(
187 &self,
188 ) -> Result<Vec<Box<dyn RemotePullRequest>>> {
189 Ok(self
190 .fetch::<GitHubPullRequest>(0)
191 .await?
192 .into_iter()
193 .map(|v| Box::new(v) as Box<dyn RemotePullRequest>)
194 .collect())
195 }
196}
197
198#[cfg(test)]
199mod test {
200 use super::*;
201 use crate::remote::RemoteCommit;
202 use pretty_assertions::assert_eq;
203
204 #[test]
205 fn timestamp() {
206 let remote_commit = GitHubCommit {
207 sha: String::from("1d244937ee6ceb8e0314a4a201ba93a7a61f2071"),
208 author: Some(GitHubCommitAuthor {
209 login: Some(String::from("orhun")),
210 }),
211 commit: Some(GitHubCommitDetails {
212 author: GitHubCommitDetailsAuthor {
213 date: String::from("2021-07-18T15:14:39+03:00"),
214 },
215 }),
216 };
217
218 assert_eq!(Some(1626610479), remote_commit.timestamp());
219 }
220}