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(
48		_id: i64,
49		api_url: &str,
50		remote: &Remote,
51		ref_name: Option<&str>,
52		page: i32,
53	) -> String {
54		let mut url = format!(
55			"{}/api/v1/repos/{}/{}/commits?limit={MAX_PAGE_SIZE}&page={page}",
56			api_url, remote.owner, remote.repo
57		);
58
59		if let Some(ref_name) = ref_name {
60			url.push_str(&format!("&sha={}", ref_name));
61		}
62
63		url
64	}
65
66	fn buffer_size() -> usize {
67		10
68	}
69
70	fn early_exit(&self) -> bool {
71		false
72	}
73}
74
75/// Author of the commit.
76#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
77pub struct GiteaCommitAuthor {
78	/// Username.
79	pub login: Option<String>,
80}
81
82/// Label of the pull request.
83#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
84#[serde(rename_all = "camelCase")]
85pub struct PullRequestLabel {
86	/// Name of the label.
87	pub name: String,
88}
89
90/// Representation of a single pull request.
91#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
92pub struct GiteaPullRequest {
93	/// Pull request number.
94	pub number:           i64,
95	/// Pull request title.
96	pub title:            Option<String>,
97	/// SHA of the merge commit.
98	pub merge_commit_sha: Option<String>,
99	/// Labels of the pull request.
100	pub labels:           Vec<PullRequestLabel>,
101}
102
103impl RemotePullRequest for GiteaPullRequest {
104	fn number(&self) -> i64 {
105		self.number
106	}
107
108	fn title(&self) -> Option<String> {
109		self.title.clone()
110	}
111
112	fn labels(&self) -> Vec<String> {
113		self.labels.iter().map(|v| v.name.clone()).collect()
114	}
115
116	fn merge_commit(&self) -> Option<String> {
117		self.merge_commit_sha.clone()
118	}
119}
120
121impl RemoteEntry for GiteaPullRequest {
122	fn url(
123		_id: i64,
124		api_url: &str,
125		remote: &Remote,
126		_ref_name: Option<&str>,
127		page: i32,
128	) -> String {
129		format!(
130			"{}/api/v1/repos/{}/{}/pulls?limit={MAX_PAGE_SIZE}&page={page}&\
131			 state=closed",
132			api_url, remote.owner, remote.repo
133		)
134	}
135
136	fn buffer_size() -> usize {
137		5
138	}
139
140	fn early_exit(&self) -> bool {
141		false
142	}
143}
144
145/// HTTP client for handling Gitea REST API requests.
146#[derive(Debug, Clone)]
147pub struct GiteaClient {
148	/// Remote.
149	remote: Remote,
150	/// HTTP client.
151	client: ClientWithMiddleware,
152}
153
154/// Constructs a Gitea client from the remote configuration.
155impl TryFrom<Remote> for GiteaClient {
156	type Error = Error;
157	fn try_from(remote: Remote) -> Result<Self> {
158		Ok(Self {
159			client: remote.create_client("application/json")?,
160			remote,
161		})
162	}
163}
164
165impl RemoteClient for GiteaClient {
166	const API_URL: &'static str = "https://codeberg.org";
167	const API_URL_ENV: &'static str = "GITEA_API_URL";
168
169	fn remote(&self) -> Remote {
170		self.remote.clone()
171	}
172
173	fn client(&self) -> ClientWithMiddleware {
174		self.client.clone()
175	}
176}
177
178impl GiteaClient {
179	/// Fetches the Gitea API and returns the commits.
180	pub async fn get_commits(
181		&self,
182		ref_name: Option<&str>,
183	) -> Result<Vec<Box<dyn RemoteCommit>>> {
184		Ok(self
185			.fetch::<GiteaCommit>(0, ref_name)
186			.await?
187			.into_iter()
188			.map(|v| Box::new(v) as Box<dyn RemoteCommit>)
189			.collect())
190	}
191
192	/// Fetches the Gitea API and returns the pull requests.
193	pub async fn get_pull_requests(
194		&self,
195		ref_name: Option<&str>,
196	) -> Result<Vec<Box<dyn RemotePullRequest>>> {
197		Ok(self
198			.fetch::<GiteaPullRequest>(0, ref_name)
199			.await?
200			.into_iter()
201			.map(|v| Box::new(v) as Box<dyn RemotePullRequest>)
202			.collect())
203	}
204}
205
206#[cfg(test)]
207mod test {
208	use super::*;
209	use crate::remote::RemoteCommit;
210	use pretty_assertions::assert_eq;
211
212	#[test]
213	fn timestamp() {
214		let remote_commit = GiteaCommit {
215			sha:     String::from("1d244937ee6ceb8e0314a4a201ba93a7a61f2071"),
216			author:  Some(GiteaCommitAuthor {
217				login: Some(String::from("orhun")),
218			}),
219			created: String::from("2021-07-18T15:14:39+03:00"),
220		};
221
222		assert_eq!(Some(1626610479), remote_commit.timestamp());
223	}
224}