git_cliff_core/remote/
gitea.rs

1use crate::config::Remote;
2use crate::error::*;
3use reqwest_middleware::ClientWithMiddleware;
4use serde::{
5	Deserialize,
6	Serialize,
7};
8
9use super::*;
10
11/// Log message to show while fetching data from Gitea.
12pub const START_FETCHING_MSG: &str = "Retrieving data from Gitea...";
13
14/// Log message to show when done fetching from Gitea.
15pub const FINISHED_FETCHING_MSG: &str = "Done fetching Gitea data.";
16
17/// Template variables related to this remote.
18pub(crate) const TEMPLATE_VARIABLES: &[&str] =
19	&["gitea", "commit.gitea", "commit.remote"];
20
21/// Representation of a single commit.
22#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
23pub struct GiteaCommit {
24	/// SHA.
25	pub sha:     String,
26	/// Author of the commit.
27	pub author:  Option<GiteaCommitAuthor>,
28	/// Timestamp of the commit.
29	pub created: String,
30}
31
32impl RemoteCommit for GiteaCommit {
33	fn id(&self) -> String {
34		self.sha.clone()
35	}
36
37	fn username(&self) -> Option<String> {
38		self.author.clone().and_then(|v| v.login)
39	}
40
41	fn timestamp(&self) -> Option<i64> {
42		Some(self.convert_to_unix_timestamp(self.created.clone().as_str()))
43	}
44}
45
46impl RemoteEntry for GiteaCommit {
47	fn url(_id: i64, api_url: &str, remote: &Remote, page: i32) -> String {
48		format!(
49			"{}/api/v1/repos/{}/{}/commits?limit={MAX_PAGE_SIZE}&page={page}",
50			api_url, remote.owner, remote.repo
51		)
52	}
53	fn buffer_size() -> usize {
54		10
55	}
56
57	fn early_exit(&self) -> bool {
58		false
59	}
60}
61
62/// Author of the commit.
63#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
64pub struct GiteaCommitAuthor {
65	/// Username.
66	pub login: Option<String>,
67}
68
69/// Label of the pull request.
70#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
71#[serde(rename_all = "camelCase")]
72pub struct PullRequestLabel {
73	/// Name of the label.
74	pub name: String,
75}
76
77/// Representation of a single pull request.
78#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
79pub struct GiteaPullRequest {
80	/// Pull request number.
81	pub number:           i64,
82	/// Pull request title.
83	pub title:            Option<String>,
84	/// SHA of the merge commit.
85	pub merge_commit_sha: Option<String>,
86	/// Labels of the pull request.
87	pub labels:           Vec<PullRequestLabel>,
88}
89
90impl RemotePullRequest for GiteaPullRequest {
91	fn number(&self) -> i64 {
92		self.number
93	}
94
95	fn title(&self) -> Option<String> {
96		self.title.clone()
97	}
98
99	fn labels(&self) -> Vec<String> {
100		self.labels.iter().map(|v| v.name.clone()).collect()
101	}
102
103	fn merge_commit(&self) -> Option<String> {
104		self.merge_commit_sha.clone()
105	}
106}
107
108impl RemoteEntry for GiteaPullRequest {
109	fn url(_id: i64, api_url: &str, remote: &Remote, page: i32) -> String {
110		format!(
111			"{}/api/v1/repos/{}/{}/pulls?limit={MAX_PAGE_SIZE}&page={page}&\
112			 state=closed",
113			api_url, remote.owner, remote.repo
114		)
115	}
116
117	fn buffer_size() -> usize {
118		5
119	}
120
121	fn early_exit(&self) -> bool {
122		false
123	}
124}
125
126/// HTTP client for handling Gitea REST API requests.
127#[derive(Debug, Clone)]
128pub struct GiteaClient {
129	/// Remote.
130	remote: Remote,
131	/// HTTP client.
132	client: ClientWithMiddleware,
133}
134
135/// Constructs a Gitea client from the remote configuration.
136impl TryFrom<Remote> for GiteaClient {
137	type Error = Error;
138	fn try_from(remote: Remote) -> Result<Self> {
139		Ok(Self {
140			client: remote.create_client("application/json")?,
141			remote,
142		})
143	}
144}
145
146impl RemoteClient for GiteaClient {
147	const API_URL: &'static str = "https://codeberg.org";
148	const API_URL_ENV: &'static str = "GITEA_API_URL";
149
150	fn remote(&self) -> Remote {
151		self.remote.clone()
152	}
153
154	fn client(&self) -> ClientWithMiddleware {
155		self.client.clone()
156	}
157}
158
159impl GiteaClient {
160	/// Fetches the Gitea API and returns the commits.
161	pub async fn get_commits(&self) -> Result<Vec<Box<dyn RemoteCommit>>> {
162		Ok(self
163			.fetch::<GiteaCommit>(0)
164			.await?
165			.into_iter()
166			.map(|v| Box::new(v) as Box<dyn RemoteCommit>)
167			.collect())
168	}
169
170	/// Fetches the Gitea API and returns the pull requests.
171	pub async fn get_pull_requests(
172		&self,
173	) -> Result<Vec<Box<dyn RemotePullRequest>>> {
174		Ok(self
175			.fetch::<GiteaPullRequest>(0)
176			.await?
177			.into_iter()
178			.map(|v| Box::new(v) as Box<dyn RemotePullRequest>)
179			.collect())
180	}
181}
182
183#[cfg(test)]
184mod test {
185	use super::*;
186	use crate::remote::RemoteCommit;
187	use pretty_assertions::assert_eq;
188
189	#[test]
190	fn timestamp() {
191		let remote_commit = GiteaCommit {
192			sha:     String::from("1d244937ee6ceb8e0314a4a201ba93a7a61f2071"),
193			author:  Some(GiteaCommitAuthor {
194				login: Some(String::from("orhun")),
195			}),
196			created: String::from("2021-07-18T15:14:39+03:00"),
197		};
198
199		assert_eq!(Some(1626610479), remote_commit.timestamp());
200	}
201}