bytes_radar/net/providers/
codeberg.rs1use crate::net::traits::{GitProvider, ParsedRepository, ProviderConfig};
2use async_trait::async_trait;
3use reqwest::Client;
4use std::collections::HashMap;
5
6pub struct CodebergProvider {
7 credentials: HashMap<String, String>,
8}
9
10impl CodebergProvider {
11 pub fn new() -> Self {
12 Self {
13 credentials: HashMap::new(),
14 }
15 }
16}
17
18#[async_trait]
19impl GitProvider for CodebergProvider {
20 fn name(&self) -> &'static str {
21 "codeberg"
22 }
23
24 fn can_handle(&self, url: &str) -> bool {
25 url.contains("codeberg.org")
26 }
27
28 fn parse_url(&self, url: &str) -> Option<ParsedRepository> {
29 if !self.can_handle(url) {
30 return None;
31 }
32
33 let url = url.trim_end_matches('/');
34
35 if url.contains("/commit/") {
36 return self.parse_commit_url(url);
37 }
38
39 if url.contains("/src/branch/") {
40 return self.parse_branch_url(url);
41 }
42
43 self.parse_basic_url(url)
44 }
45
46 fn build_download_urls(&self, parsed: &ParsedRepository) -> Vec<String> {
47 let mut urls = Vec::new();
48
49 if let Some(ref branch_or_commit) = parsed.branch_or_commit {
50 urls.push(format!(
51 "https://codeberg.org/{}/{}/archive/{}.tar.gz",
52 parsed.owner, parsed.repo, branch_or_commit
53 ));
54 }
55
56 urls
57 }
58
59 async fn get_default_branch(
60 &self,
61 _client: &Client,
62 _parsed: &ParsedRepository,
63 ) -> Option<String> {
64 None
65 }
66
67 fn apply_config(&mut self, config: &ProviderConfig) {
68 self.credentials = config.credentials.clone();
69 }
70
71 fn get_project_name(&self, url: &str) -> String {
72 if let Some(parsed) = self.parse_url(url) {
73 return parsed.project_name;
74 }
75
76 if let Some(filename) = url.split('/').next_back() {
77 if filename.ends_with(".tar.gz") {
78 return filename.trim_end_matches(".tar.gz").to_string();
79 }
80 if filename.ends_with(".tgz") {
81 return filename.trim_end_matches(".tgz").to_string();
82 }
83 return filename.to_string();
84 }
85
86 "codeberg-project".to_string()
87 }
88}
89
90impl CodebergProvider {
91 fn parse_commit_url(&self, url: &str) -> Option<ParsedRepository> {
92 let parts: Vec<&str> = url.split('/').collect();
93 if let Some(commit_pos) = parts.iter().position(|&x| x == "commit") {
94 if commit_pos + 1 < parts.len() && commit_pos >= 2 {
95 let owner = parts[commit_pos - 2].to_string();
96 let repo = parts[commit_pos - 1].to_string();
97 let commit = parts[commit_pos + 1].to_string();
98
99 return Some(
100 ParsedRepository::new(owner, repo)
101 .with_commit(commit)
102 .with_host("codeberg.org".to_string()),
103 );
104 }
105 }
106 None
107 }
108
109 fn parse_branch_url(&self, url: &str) -> Option<ParsedRepository> {
110 let parts: Vec<&str> = url.split('/').collect();
111 if let Some(branch_pos) = parts.iter().position(|&x| x == "branch") {
112 if branch_pos + 1 < parts.len() && branch_pos >= 3 {
113 let owner = parts[branch_pos - 3].to_string();
114 let repo = parts[branch_pos - 2].to_string();
115 let branch = parts[branch_pos + 1].to_string();
116
117 return Some(
118 ParsedRepository::new(owner, repo)
119 .with_branch(branch)
120 .with_host("codeberg.org".to_string()),
121 );
122 }
123 }
124 None
125 }
126
127 fn parse_basic_url(&self, url: &str) -> Option<ParsedRepository> {
128 let parts: Vec<&str> = url.split('/').collect();
129 if let Some(codeberg_pos) = parts.iter().position(|&x| x == "codeberg.org") {
130 if codeberg_pos + 2 < parts.len() {
131 let owner = parts[codeberg_pos + 1].to_string();
132 let repo = parts[codeberg_pos + 2].to_string();
133
134 return Some(
135 ParsedRepository::new(owner, repo).with_host("codeberg.org".to_string()),
136 );
137 }
138 }
139 None
140 }
141}
142
143impl Default for CodebergProvider {
144 fn default() -> Self {
145 Self::new()
146 }
147}
148
149#[cfg(test)]
150mod tests {
151 use super::*;
152
153 #[test]
154 fn test_can_handle() {
155 let provider = CodebergProvider::new();
156 assert!(provider.can_handle("https://codeberg.org/user/repo"));
157 assert!(provider.can_handle("https://codeberg.org/user/repo/commit/abc123"));
158 assert!(!provider.can_handle("https://github.com/user/repo"));
159 }
160
161 #[test]
162 fn test_parse_basic_url() {
163 let provider = CodebergProvider::new();
164
165 let parsed = provider
166 .parse_url("https://codeberg.org/user/repo")
167 .unwrap();
168 assert_eq!(parsed.owner, "user");
169 assert_eq!(parsed.repo, "repo");
170 assert_eq!(parsed.project_name, "repo@main");
171 assert_eq!(parsed.branch_or_commit, None);
172 assert!(!parsed.is_commit);
173 assert_eq!(parsed.host.as_ref().unwrap(), "codeberg.org");
174 }
175
176 #[test]
177 fn test_parse_branch_url() {
178 let provider = CodebergProvider::new();
179
180 let parsed = provider
181 .parse_url("https://codeberg.org/user/repo/src/branch/develop")
182 .unwrap();
183 assert_eq!(parsed.owner, "user");
184 assert_eq!(parsed.repo, "repo");
185 assert_eq!(parsed.project_name, "repo@develop");
186 assert_eq!(parsed.branch_or_commit, Some("develop".to_string()));
187 assert!(!parsed.is_commit);
188 }
189
190 #[test]
191 fn test_parse_commit_url() {
192 let provider = CodebergProvider::new();
193
194 let parsed = provider
195 .parse_url("https://codeberg.org/user/repo/commit/abc1234567890")
196 .unwrap();
197 assert_eq!(parsed.owner, "user");
198 assert_eq!(parsed.repo, "repo");
199 assert_eq!(parsed.project_name, "repo@abc1234");
200 assert_eq!(parsed.branch_or_commit, Some("abc1234567890".to_string()));
201 assert!(parsed.is_commit);
202 }
203
204 #[test]
205 fn test_build_download_urls() {
206 let provider = CodebergProvider::new();
207
208 let parsed = ParsedRepository::new("user".to_string(), "repo".to_string())
209 .with_branch("main".to_string());
210
211 let urls = provider.build_download_urls(&parsed);
212 assert!(urls.contains(&"https://codeberg.org/user/repo/archive/main.tar.gz".to_string()));
213 }
214}