use super::Repository; use serde::Deserialize; use crate::providers::Provider; pub struct Github { pub username: String, pub token: Option, } impl Github { pub fn new(username: String, token: Option) -> Self { Self { username, token } } } #[derive(Deserialize, Debug)] struct GithubRepoDto { id: u64, name: String, owner: GithubOwnerDto, ssh_url: Option, clone_url: String, stargazers_count: u64, description: Option, } #[derive(Deserialize, Debug)] struct GithubOwnerDto { login: String, } impl From for Repository { fn from(gh: GithubRepoDto) -> Self { Repository { id: gh.id, name: gh.name, owner: gh.owner.login, ssh_url: gh.ssh_url, https_url: gh.clone_url, stars: gh.stargazers_count, description: gh.description, } } } impl Provider for Github { async fn repositories(&self) -> super::Result> { let client = reqwest::Client::new(); let mut page = 1; let mut repos: Vec = Vec::new(); let base_url = match self.token.is_some() { true => "https://api.github.com/user/repos".to_string(), false => format!("https://api.github.com/users/{}/repos", self.username), }; loop { let url = format!("{}?per_page=100&page={}", base_url, page); let mut request = client .get(&url) .header("User-Agent", "my-app") .header("Accept", "application/vnd.github+json"); // Best practice for GitHub API // Apply token authentication if it's available if let Some(ref token) = self.token { request = request.bearer_auth(token); } let response = request.send().await?; let status = response.status(); if !status.is_success() { let error_text = response.text().await?; let err = format!("Github API Error ({}): {}", status, error_text); return Err(err.into()); } let gh_page: Vec = response.json().await?; if gh_page.is_empty() { break; } let domain_repos = gh_page.into_iter().map(Repository::from); repos.extend(domain_repos); page += 1; } Ok(repos) } }