Test message 123!
This commit is contained in:
1 parent
4cf726709f
commit
d4fdc9890a
52 files changed
+5537
-277
No files matched your search
@@ -188,4 +188,31 @@ impl Provider for GiteaProvider {
|
||||
let v = Self::check(resp).await.context("whoami")?;
|
||||
Ok(v["login"].as_str().unwrap_or("unknown").to_string())
|
||||
}
|
||||
|
||||
async fn update_visibility(&self, repo: &str, private: bool) -> Result<()> {
|
||||
let body = json!({ "private": private });
|
||||
let resp = self.client
|
||||
.patch(self.url(&format!("repos/{}/{}", self.owner, repo)))
|
||||
.json(&body).send().await?;
|
||||
Self::check(resp).await.context("update_visibility")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn rename_repo(&self, repo: &str, new_name: &str) -> Result<()> {
|
||||
let body = json!({ "name": new_name });
|
||||
let resp = self.client
|
||||
.patch(self.url(&format!("repos/{}/{}", self.owner, repo)))
|
||||
.json(&body).send().await?;
|
||||
Self::check(resp).await.context("rename_repo")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn archive_repo(&self, repo: &str) -> Result<()> {
|
||||
let body = json!({ "archived": true });
|
||||
let resp = self.client
|
||||
.patch(self.url(&format!("repos/{}/{}", self.owner, repo)))
|
||||
.json(&body).send().await?;
|
||||
Self::check(resp).await.context("archive_repo")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
+93
-1
@@ -1,4 +1,4 @@
|
||||
use super::{PrInfo, Provider, RepoInfo, RepoMeta};
|
||||
use super::{IssueDetail, PrDetail, PrInfo, Provider, RepoInfo, RepoMeta};
|
||||
use anyhow::{Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use reqwest::{header, Client, Response};
|
||||
@@ -179,4 +179,96 @@ impl Provider for GitHubProvider {
|
||||
let v = Self::check(resp).await.context("whoami")?;
|
||||
Ok(v["login"].as_str().unwrap_or("unknown").to_string())
|
||||
}
|
||||
|
||||
async fn update_visibility(&self, repo: &str, private: bool) -> Result<()> {
|
||||
let body = serde_json::json!({ "private": private });
|
||||
let resp = self.client
|
||||
.patch(self.url(&format!("repos/{}/{}", self.owner, repo)))
|
||||
.json(&body).send().await?;
|
||||
Self::check(resp).await.context("update_visibility")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn rename_repo(&self, repo: &str, new_name: &str) -> Result<()> {
|
||||
let body = serde_json::json!({ "name": new_name });
|
||||
let resp = self.client
|
||||
.patch(self.url(&format!("repos/{}/{}", self.owner, repo)))
|
||||
.json(&body).send().await?;
|
||||
Self::check(resp).await.context("rename_repo")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn archive_repo(&self, repo: &str) -> Result<()> {
|
||||
let body = serde_json::json!({ "archived": true });
|
||||
let resp = self.client
|
||||
.patch(self.url(&format!("repos/{}/{}", self.owner, repo)))
|
||||
.json(&body).send().await?;
|
||||
Self::check(resp).await.context("archive_repo")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn list_pull_requests(&self, repo: &str) -> Result<Vec<PrDetail>> {
|
||||
let resp = self.client
|
||||
.get(self.url(&format!("repos/{}/{}/pulls?state=open&per_page=100", self.owner, repo)))
|
||||
.send().await?;
|
||||
if !resp.status().is_success() { return Ok(vec![]); }
|
||||
let list: Value = resp.json().await.unwrap_or(Value::Array(vec![]));
|
||||
let prs = list.as_array().map(|arr| arr.iter().map(|pr| {
|
||||
let labels: Vec<String> = pr["labels"].as_array()
|
||||
.map(|l| l.iter().filter_map(|x| x["name"].as_str().map(|s| s.to_string())).collect())
|
||||
.unwrap_or_default();
|
||||
PrDetail {
|
||||
repo: repo.to_string(),
|
||||
number: pr["number"].as_u64().unwrap_or(0),
|
||||
title: pr["title"].as_str().unwrap_or("").to_string(),
|
||||
author: pr["user"]["login"].as_str().unwrap_or("").to_string(),
|
||||
head_branch: pr["head"]["ref"].as_str().unwrap_or("").to_string(),
|
||||
base_branch: pr["base"]["ref"].as_str().unwrap_or("").to_string(),
|
||||
created_ts: parse_gh_ts(pr["created_at"].as_str().unwrap_or("")),
|
||||
updated_ts: parse_gh_ts(pr["updated_at"].as_str().unwrap_or("")),
|
||||
url: pr["html_url"].as_str().unwrap_or("").to_string(),
|
||||
draft: pr["draft"].as_bool().unwrap_or(false),
|
||||
labels,
|
||||
}
|
||||
}).collect()).unwrap_or_default();
|
||||
Ok(prs)
|
||||
}
|
||||
|
||||
async fn list_issues(&self, repo: &str) -> Result<Vec<IssueDetail>> {
|
||||
let resp = self.client
|
||||
.get(self.url(&format!(
|
||||
"repos/{}/{}/issues?state=open&per_page=100&pulls=false",
|
||||
self.owner, repo
|
||||
)))
|
||||
.send().await?;
|
||||
if !resp.status().is_success() { return Ok(vec![]); }
|
||||
let list: Value = resp.json().await.unwrap_or(Value::Array(vec![]));
|
||||
let issues = list.as_array().map(|arr| arr.iter()
|
||||
// GitHub /issues returns PRs too — filter them out
|
||||
.filter(|i| i["pull_request"].is_null())
|
||||
.map(|i| {
|
||||
let labels: Vec<String> = i["labels"].as_array()
|
||||
.map(|l| l.iter().filter_map(|x| x["name"].as_str().map(|s| s.to_string())).collect())
|
||||
.unwrap_or_default();
|
||||
IssueDetail {
|
||||
repo: repo.to_string(),
|
||||
number: i["number"].as_u64().unwrap_or(0),
|
||||
title: i["title"].as_str().unwrap_or("").to_string(),
|
||||
author: i["user"]["login"].as_str().unwrap_or("").to_string(),
|
||||
labels,
|
||||
created_ts: parse_gh_ts(i["created_at"].as_str().unwrap_or("")),
|
||||
updated_ts: parse_gh_ts(i["updated_at"].as_str().unwrap_or("")),
|
||||
url: i["html_url"].as_str().unwrap_or("").to_string(),
|
||||
comments: i["comments"].as_u64().unwrap_or(0) as u32,
|
||||
}
|
||||
}).collect()
|
||||
).unwrap_or_default();
|
||||
Ok(issues)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_gh_ts(s: &str) -> i64 {
|
||||
chrono::DateTime::parse_from_rfc3339(s)
|
||||
.map(|dt| dt.timestamp())
|
||||
.unwrap_or(0)
|
||||
}
|
||||
@@ -190,4 +190,31 @@ impl Provider for GitLabProvider {
|
||||
let v = Self::check(resp).await.context("whoami")?;
|
||||
Ok(v["username"].as_str().unwrap_or("unknown").to_string())
|
||||
}
|
||||
|
||||
async fn update_visibility(&self, repo: &str, private: bool) -> Result<()> {
|
||||
let visibility = if private { "private" } else { "public" };
|
||||
let body = json!({ "visibility": visibility });
|
||||
let resp = self.client
|
||||
.put(self.url(&format!("projects/{}", self.repo_id(repo))))
|
||||
.json(&body).send().await?;
|
||||
Self::check(resp).await.context("update_visibility")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn rename_repo(&self, repo: &str, new_name: &str) -> Result<()> {
|
||||
let body = json!({ "name": new_name, "path": new_name });
|
||||
let resp = self.client
|
||||
.put(self.url(&format!("projects/{}", self.repo_id(repo))))
|
||||
.json(&body).send().await?;
|
||||
Self::check(resp).await.context("rename_repo")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn archive_repo(&self, repo: &str) -> Result<()> {
|
||||
let resp = self.client
|
||||
.post(self.url(&format!("projects/{}/archive", self.repo_id(repo))))
|
||||
.send().await?;
|
||||
Self::check(resp).await.context("archive_repo")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,34 @@ pub struct RepoMeta {
|
||||
pub stars: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct PrDetail {
|
||||
pub repo: String,
|
||||
pub number: u64,
|
||||
pub title: String,
|
||||
pub author: String,
|
||||
pub head_branch: String,
|
||||
pub base_branch: String,
|
||||
pub created_ts: i64,
|
||||
pub updated_ts: i64,
|
||||
pub url: String,
|
||||
pub draft: bool,
|
||||
pub labels: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct IssueDetail {
|
||||
pub repo: String,
|
||||
pub number: u64,
|
||||
pub title: String,
|
||||
pub author: String,
|
||||
pub labels: Vec<String>,
|
||||
pub created_ts: i64,
|
||||
pub updated_ts: i64,
|
||||
pub url: String,
|
||||
pub comments: u32,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
#[allow(dead_code)]
|
||||
pub trait Provider: Send + Sync {
|
||||
@@ -79,8 +107,36 @@ pub trait Provider: Send + Sync {
|
||||
/// Fetch basic metadata (issues, PRs, stars).
|
||||
async fn repo_meta(&self, repo: &str) -> Result<RepoMeta>;
|
||||
|
||||
/// List open pull requests for a repository.
|
||||
async fn list_pull_requests(&self, _repo: &str) -> Result<Vec<PrDetail>> {
|
||||
Ok(vec![])
|
||||
}
|
||||
|
||||
/// List open issues for a repository (excludes PRs).
|
||||
async fn list_issues(&self, _repo: &str) -> Result<Vec<IssueDetail>> {
|
||||
Ok(vec![])
|
||||
}
|
||||
|
||||
/// Verify the token works, return the authenticated username.
|
||||
async fn whoami(&self) -> Result<String>;
|
||||
|
||||
/// Change a repository's visibility.
|
||||
async fn update_visibility(&self, repo: &str, private: bool) -> Result<()> {
|
||||
let _ = (repo, private);
|
||||
anyhow::bail!("update_visibility not supported by this provider")
|
||||
}
|
||||
|
||||
/// Rename a repository on the remote.
|
||||
async fn rename_repo(&self, repo: &str, new_name: &str) -> Result<()> {
|
||||
let _ = (repo, new_name);
|
||||
anyhow::bail!("rename_repo not supported by this provider")
|
||||
}
|
||||
|
||||
/// Archive a repository (make it read-only).
|
||||
async fn archive_repo(&self, repo: &str) -> Result<()> {
|
||||
let _ = repo;
|
||||
anyhow::bail!("archive_repo not supported by this provider")
|
||||
}
|
||||
}
|
||||
|
||||
/// Build a boxed `Provider` from an `Account`.
|
||||
|
||||
Reference in new issue
Block a user