source/github: implement
This commit is contained in:
@@ -92,7 +92,6 @@ impl Default for RepositoryFilter {
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct RepositoryInfo {
|
||||
pub name: String,
|
||||
pub full_name: String,
|
||||
pub url: String,
|
||||
pub icon_url: Option<String>,
|
||||
pub is_fork: bool,
|
||||
|
||||
@@ -25,7 +25,10 @@ impl KagamiSource for KagamiSourceGitea {
|
||||
const DEFAULT_HOST: &'static str = "gitea.com";
|
||||
|
||||
fn api_base(&self) -> String {
|
||||
format!("https://{}", self.host.as_deref().unwrap_or(Self::DEFAULT_HOST))
|
||||
format!(
|
||||
"https://{}",
|
||||
self.host.as_deref().unwrap_or(Self::DEFAULT_HOST)
|
||||
)
|
||||
}
|
||||
|
||||
async fn get_repositories_by_user(
|
||||
@@ -61,7 +64,6 @@ impl KagamiSource for KagamiSourceGitea {
|
||||
for r in repos_resp.iter() {
|
||||
let info = RepositoryInfo {
|
||||
name: r.name.clone(),
|
||||
full_name: r.full_name.clone(),
|
||||
url: r.html_url.clone(),
|
||||
icon_url: if r.avatar_url.is_empty() {
|
||||
None
|
||||
@@ -106,7 +108,7 @@ impl KagamiSource for KagamiSourceGitea {
|
||||
) -> anyhow::Result<HashSet<RepositoryInfo>> {
|
||||
let base = self.api_base();
|
||||
|
||||
let client: GiteaClient = match token {
|
||||
let client: GiteaClient = match token {
|
||||
Some(tok) if !tok.is_empty() => GiteaClient::new(&base, Token(tok.to_string())),
|
||||
_ => GiteaClient::new(&base, gitea_sdk::Auth::<String>::None),
|
||||
};
|
||||
@@ -131,7 +133,6 @@ impl KagamiSource for KagamiSourceGitea {
|
||||
for r in repos_resp.iter() {
|
||||
let info = RepositoryInfo {
|
||||
name: r.name.clone(),
|
||||
full_name: r.full_name.clone(),
|
||||
url: r.html_url.clone(),
|
||||
icon_url: if r.avatar_url.is_empty() {
|
||||
None
|
||||
@@ -199,7 +200,6 @@ impl KagamiSource for KagamiSourceGitea {
|
||||
for r in repos_resp.iter() {
|
||||
let info = RepositoryInfo {
|
||||
name: r.name.clone(),
|
||||
full_name: r.full_name.clone(),
|
||||
url: r.html_url.clone(),
|
||||
icon_url: if r.avatar_url.is_empty() {
|
||||
None
|
||||
|
||||
@@ -1,11 +1,215 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use anyhow::Context;
|
||||
use octocrab::{Octocrab, Page, models};
|
||||
|
||||
use crate::sources::{KagamiSource, RepositoryFilter, RepositoryInfo, RepositoryVisibility};
|
||||
|
||||
pub struct KagamiSourceGithub {
|
||||
host: String,
|
||||
host: Option<String>,
|
||||
}
|
||||
|
||||
impl KagamiSourceGithub {
|
||||
pub fn new(host: &str) -> Self {
|
||||
Self {
|
||||
host: host.to_string(),
|
||||
pub fn new(host: Option<&str>) -> Self {
|
||||
Self {
|
||||
host: host.map(|s| s.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl KagamiSource for KagamiSourceGithub {
|
||||
const NAME: &'static str = "github";
|
||||
const DEFAULT_HOST: &'static str = "github.com";
|
||||
|
||||
fn api_base(&self) -> String {
|
||||
match self.host.as_deref() {
|
||||
Some(host) if host != "github.com" => format!("https://{}/api/v3", host),
|
||||
_ => "https://api.github.com".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_repositories_by_user(
|
||||
&self,
|
||||
owner: &str,
|
||||
token: Option<&str>,
|
||||
_filter: &RepositoryFilter,
|
||||
) -> anyhow::Result<HashSet<RepositoryInfo>> {
|
||||
let octocrab = {
|
||||
let mut builder = Octocrab::builder();
|
||||
if let Some(t) = token {
|
||||
builder = builder.personal_token(t.to_owned());
|
||||
}
|
||||
if self.host.is_some() {
|
||||
builder = builder
|
||||
.base_uri(self.api_base())
|
||||
.context("failed to set octocrab base_uri")?;
|
||||
}
|
||||
builder.build().context("failed to build octocrab client")?
|
||||
};
|
||||
|
||||
let mut page: Option<Page<models::Repository>> = None;
|
||||
let mut repos = HashSet::new();
|
||||
|
||||
loop {
|
||||
let current: Page<models::Repository> = match page {
|
||||
None => octocrab
|
||||
.get(
|
||||
&format!("/users/{}/repos", owner),
|
||||
Some(&[("per_page", "100")]),
|
||||
)
|
||||
.await
|
||||
.context("failed to fetch user repositories")?,
|
||||
Some(ref p) => {
|
||||
match octocrab.get_page(&p.next).await {
|
||||
Ok(Some(next_page)) => next_page,
|
||||
Ok(None) => break,
|
||||
Err(_) => break,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for r in ¤t.items {
|
||||
let ri = map_github_repo_to_info(r);
|
||||
repos.insert(ri);
|
||||
}
|
||||
|
||||
if current.next.is_none() {
|
||||
break;
|
||||
} else {
|
||||
page = Some(current);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(repos)
|
||||
}
|
||||
|
||||
async fn get_repositories_by_organization(
|
||||
&self,
|
||||
owner: &str,
|
||||
token: Option<&str>,
|
||||
// NOTE: GitHub nested teams doesn't remove the organization repos; this flag is unused.
|
||||
_recurse_groups: bool,
|
||||
_filter: &RepositoryFilter,
|
||||
) -> anyhow::Result<HashSet<RepositoryInfo>> {
|
||||
let octocrab = {
|
||||
let mut builder = Octocrab::builder();
|
||||
if let Some(t) = token {
|
||||
builder = builder.personal_token(t.to_owned());
|
||||
}
|
||||
if self.host.is_some() {
|
||||
builder = builder
|
||||
.base_uri(self.api_base())
|
||||
.context("failed to set octocrab base_uri")?;
|
||||
}
|
||||
builder.build().context("failed to build octocrab client")?
|
||||
};
|
||||
|
||||
let mut repos = HashSet::new();
|
||||
let mut page: Option<Page<models::Repository>> = None;
|
||||
|
||||
loop {
|
||||
let current: Page<models::Repository> = match page {
|
||||
None => octocrab
|
||||
.get(
|
||||
&format!("/orgs/{}/repos", owner),
|
||||
Some(&[("per_page", "100")]),
|
||||
)
|
||||
.await
|
||||
.context("failed to fetch organization repositories")?,
|
||||
Some(ref p) => match octocrab.get_page(&p.next).await {
|
||||
Ok(Some(next_page)) => next_page,
|
||||
Ok(None) => break,
|
||||
Err(_) => break,
|
||||
},
|
||||
};
|
||||
|
||||
for r in ¤t.items {
|
||||
let ri = map_github_repo_to_info(r);
|
||||
repos.insert(ri);
|
||||
}
|
||||
|
||||
if current.next.is_none() {
|
||||
break;
|
||||
} else {
|
||||
page = Some(current);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(repos)
|
||||
}
|
||||
|
||||
async fn get_repositories_by_stars(
|
||||
&self,
|
||||
owner: &str,
|
||||
token: Option<&str>,
|
||||
_filter: &RepositoryFilter,
|
||||
) -> anyhow::Result<HashSet<RepositoryInfo>> {
|
||||
let octocrab = {
|
||||
let mut builder = Octocrab::builder();
|
||||
if let Some(t) = token {
|
||||
builder = builder.personal_token(t.to_owned());
|
||||
}
|
||||
if self.host.is_some() {
|
||||
builder = builder
|
||||
.base_uri(self.api_base())
|
||||
.context("failed to set octocrab base_uri")?;
|
||||
}
|
||||
builder.build().context("failed to build octocrab client")?
|
||||
};
|
||||
|
||||
let mut page: Option<Page<models::Repository>> = None;
|
||||
let mut repos = HashSet::new();
|
||||
|
||||
loop {
|
||||
let current: Page<models::Repository> = match page {
|
||||
None => octocrab
|
||||
.get(
|
||||
&format!("/users/{}/starred", owner),
|
||||
Some(&[("per_page", "100")]),
|
||||
)
|
||||
.await
|
||||
.context("failed to fetch starred repositories")?,
|
||||
Some(ref p) => match octocrab.get_page(&p.next).await {
|
||||
Ok(Some(next_page)) => next_page,
|
||||
Ok(None) => break,
|
||||
Err(_) => break,
|
||||
},
|
||||
};
|
||||
|
||||
for r in ¤t.items {
|
||||
let ri = map_github_repo_to_info(r);
|
||||
repos.insert(ri);
|
||||
}
|
||||
|
||||
if current.next.is_none() {
|
||||
break;
|
||||
} else {
|
||||
page = Some(current);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(repos)
|
||||
}
|
||||
}
|
||||
|
||||
fn map_github_repo_to_info(r: &models::Repository) -> RepositoryInfo {
|
||||
RepositoryInfo {
|
||||
name: r.name.clone(),
|
||||
// TODO: don't unwrap with string default here
|
||||
url: r
|
||||
.html_url
|
||||
.clone()
|
||||
.map(|u| u.to_string())
|
||||
.unwrap_or_default(),
|
||||
icon_url: r.owner.as_ref().map(|o| o.avatar_url.to_string()),
|
||||
is_fork: r.fork.unwrap_or(false),
|
||||
is_archived: r.archived.unwrap_or(false),
|
||||
visibility: if r.private.unwrap_or(false) {
|
||||
RepositoryVisibility::Private
|
||||
} else {
|
||||
RepositoryVisibility::Public
|
||||
},
|
||||
stars: r.stargazers_count.unwrap_or(0),
|
||||
description: r.description.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user