1use std::sync::Arc;
2
3use anyhow::Result;
4use async_trait::async_trait;
5use url::Url;
6use util::codeberg;
7use util::http::HttpClient;
8
9use crate::{
10 BuildCommitPermalinkParams, BuildPermalinkParams, GitHostingProvider, Oid, ParsedGitRemote,
11};
12
13pub struct Codeberg;
14
15#[async_trait]
16impl GitHostingProvider for Codeberg {
17 fn name(&self) -> String {
18 "Codeberg".to_string()
19 }
20
21 fn base_url(&self) -> Url {
22 Url::parse("https://codeberg.org").unwrap()
23 }
24
25 fn supports_avatars(&self) -> bool {
26 true
27 }
28
29 fn format_line_number(&self, line: u32) -> String {
30 format!("L{line}")
31 }
32
33 fn format_line_numbers(&self, start_line: u32, end_line: u32) -> String {
34 format!("L{start_line}-L{end_line}")
35 }
36
37 fn parse_remote_url<'a>(&self, url: &'a str) -> Option<ParsedGitRemote<'a>> {
38 if url.starts_with("git@codeberg.org:") || url.starts_with("https://codeberg.org/") {
39 let repo_with_owner = url
40 .trim_start_matches("git@codeberg.org:")
41 .trim_start_matches("https://codeberg.org/")
42 .trim_end_matches(".git");
43
44 let (owner, repo) = repo_with_owner.split_once('/')?;
45
46 return Some(ParsedGitRemote { owner, repo });
47 }
48
49 None
50 }
51
52 fn build_commit_permalink(
53 &self,
54 remote: &ParsedGitRemote,
55 params: BuildCommitPermalinkParams,
56 ) -> Url {
57 let BuildCommitPermalinkParams { sha } = params;
58 let ParsedGitRemote { owner, repo } = remote;
59
60 self.base_url()
61 .join(&format!("{owner}/{repo}/commit/{sha}"))
62 .unwrap()
63 }
64
65 fn build_permalink(&self, remote: ParsedGitRemote, params: BuildPermalinkParams) -> Url {
66 let ParsedGitRemote { owner, repo } = remote;
67 let BuildPermalinkParams {
68 sha,
69 path,
70 selection,
71 } = params;
72
73 let mut permalink = self
74 .base_url()
75 .join(&format!("{owner}/{repo}/src/commit/{sha}/{path}"))
76 .unwrap();
77 permalink.set_fragment(
78 selection
79 .map(|selection| self.line_fragment(&selection))
80 .as_deref(),
81 );
82 permalink
83 }
84
85 async fn commit_author_avatar_url(
86 &self,
87 repo_owner: &str,
88 repo: &str,
89 commit: Oid,
90 http_client: Arc<dyn HttpClient>,
91 ) -> Result<Option<Url>> {
92 let commit = commit.to_string();
93 let avatar_url =
94 codeberg::fetch_codeberg_commit_author(repo_owner, repo, &commit, &http_client)
95 .await?
96 .map(|author| Url::parse(&author.avatar_url))
97 .transpose()?;
98 Ok(avatar_url)
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105
106 #[test]
107 fn test_build_codeberg_permalink_from_ssh_url() {
108 let remote = ParsedGitRemote {
109 owner: "rajveermalviya",
110 repo: "zed",
111 };
112 let permalink = Codeberg.build_permalink(
113 remote,
114 BuildPermalinkParams {
115 sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
116 path: "crates/editor/src/git/permalink.rs",
117 selection: None,
118 },
119 );
120
121 let expected_url = "https://codeberg.org/rajveermalviya/zed/src/commit/faa6f979be417239b2e070dbbf6392b909224e0b/crates/editor/src/git/permalink.rs";
122 assert_eq!(permalink.to_string(), expected_url.to_string())
123 }
124
125 #[test]
126 fn test_build_codeberg_permalink_from_ssh_url_single_line_selection() {
127 let remote = ParsedGitRemote {
128 owner: "rajveermalviya",
129 repo: "zed",
130 };
131 let permalink = Codeberg.build_permalink(
132 remote,
133 BuildPermalinkParams {
134 sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
135 path: "crates/editor/src/git/permalink.rs",
136 selection: Some(6..6),
137 },
138 );
139
140 let expected_url = "https://codeberg.org/rajveermalviya/zed/src/commit/faa6f979be417239b2e070dbbf6392b909224e0b/crates/editor/src/git/permalink.rs#L7";
141 assert_eq!(permalink.to_string(), expected_url.to_string())
142 }
143
144 #[test]
145 fn test_build_codeberg_permalink_from_ssh_url_multi_line_selection() {
146 let remote = ParsedGitRemote {
147 owner: "rajveermalviya",
148 repo: "zed",
149 };
150 let permalink = Codeberg.build_permalink(
151 remote,
152 BuildPermalinkParams {
153 sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
154 path: "crates/editor/src/git/permalink.rs",
155 selection: Some(23..47),
156 },
157 );
158
159 let expected_url = "https://codeberg.org/rajveermalviya/zed/src/commit/faa6f979be417239b2e070dbbf6392b909224e0b/crates/editor/src/git/permalink.rs#L24-L48";
160 assert_eq!(permalink.to_string(), expected_url.to_string())
161 }
162
163 #[test]
164 fn test_build_codeberg_permalink_from_https_url() {
165 let remote = ParsedGitRemote {
166 owner: "rajveermalviya",
167 repo: "zed",
168 };
169 let permalink = Codeberg.build_permalink(
170 remote,
171 BuildPermalinkParams {
172 sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
173 path: "crates/zed/src/main.rs",
174 selection: None,
175 },
176 );
177
178 let expected_url = "https://codeberg.org/rajveermalviya/zed/src/commit/faa6f979be417239b2e070dbbf6392b909224e0b/crates/zed/src/main.rs";
179 assert_eq!(permalink.to_string(), expected_url.to_string())
180 }
181
182 #[test]
183 fn test_build_codeberg_permalink_from_https_url_single_line_selection() {
184 let remote = ParsedGitRemote {
185 owner: "rajveermalviya",
186 repo: "zed",
187 };
188 let permalink = Codeberg.build_permalink(
189 remote,
190 BuildPermalinkParams {
191 sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
192 path: "crates/zed/src/main.rs",
193 selection: Some(6..6),
194 },
195 );
196
197 let expected_url = "https://codeberg.org/rajveermalviya/zed/src/commit/faa6f979be417239b2e070dbbf6392b909224e0b/crates/zed/src/main.rs#L7";
198 assert_eq!(permalink.to_string(), expected_url.to_string())
199 }
200
201 #[test]
202 fn test_build_codeberg_permalink_from_https_url_multi_line_selection() {
203 let remote = ParsedGitRemote {
204 owner: "rajveermalviya",
205 repo: "zed",
206 };
207 let permalink = Codeberg.build_permalink(
208 remote,
209 BuildPermalinkParams {
210 sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
211 path: "crates/zed/src/main.rs",
212 selection: Some(23..47),
213 },
214 );
215
216 let expected_url = "https://codeberg.org/rajveermalviya/zed/src/commit/faa6f979be417239b2e070dbbf6392b909224e0b/crates/zed/src/main.rs#L24-L48";
217 assert_eq!(permalink.to_string(), expected_url.to_string())
218 }
219}