Skip to main content

bytes_radar/net/providers/
codeberg.rs

1use 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}