/
basuev
/
gverse
Обзор
Документация
Войти
/
basuev
/
gverse
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/api/issues.rs
342 строки
9 KB
basuev
initial commit: gverse v0.1.0
27 май 2026, 08:52
27 май 2026, 08:52
98b24dd
Код
Авторство
О чём код?
use crate::api::split_repo; use crate::client::Client; use crate::config::Config; use crate::error::{Error, Result}; use crate::output::{Format, print_value}; use clap::Subcommand; use reqwest::Method; use serde_json::{Map, Value}; #[derive(Subcommand, Debug)] pub enum IssuesCmd { List { repo: String, #[arg(long)] state: Option<String>, #[arg(long)] labels: Option<String>, #[arg(long)] page: Option<u32>, #[arg(long)] limit: Option<u32>, }, View { repo: String, index: u64, }, Labels { repo: String, index: u64, }, Timeline { repo: String, index: u64, #[arg(long)] page: Option<u32>, #[arg(long)] limit: Option<u32>, }, #[command(subcommand)] Comment(CommentCmd), #[command(subcommand)] Reaction(ReactionCmd), } #[derive(Subcommand, Debug)] pub enum CommentCmd { List { repo: String, index: u64, #[arg(long)] page: Option<u32>, #[arg(long)] limit: Option<u32>, }, View { repo: String, id: u64, }, Create { repo: String, index: u64, #[arg(long)] body: String, }, Edit { repo: String, index: u64, id: u64, #[arg(long)] body: String, }, Delete { repo: String, index: u64, id: u64, #[arg(long)] yes: bool, }, } #[derive(Subcommand, Debug)] pub enum ReactionCmd { Add { repo: String, index: u64, comment_id: u64, #[arg(long)] content: String, }, Remove { repo: String, index: u64, comment_id: u64, reaction_id: u64, #[arg(long)] yes: bool, }, } pub async fn run(cmd: IssuesCmd, cfg: &Config, profile: Option<&str>, fmt: Format) -> Result<()> { let p = cfg.active_profile(profile)?; let client = Client::new(p)?; match cmd { IssuesCmd::List { repo, state, labels, page, limit, } => list(&client, &repo, state, labels, page, limit, fmt).await, IssuesCmd::View { repo, index } => view(&client, &repo, index, fmt).await, IssuesCmd::Labels { repo, index } => labels(&client, &repo, index, fmt).await, IssuesCmd::Timeline { repo, index, page, limit, } => timeline(&client, &repo, index, page, limit, fmt).await, IssuesCmd::Comment(c) => comment(&client, c, fmt).await, IssuesCmd::Reaction(r) => reaction(&client, r, fmt).await, } } async fn list( client: &Client, repo: &str, state: Option<String>, labels: Option<String>, page: Option<u32>, limit: Option<u32>, fmt: Format, ) -> Result<()> { let (owner, name) = split_repo(repo)?; let mut q: Vec<(&str, String)> = Vec::new(); if let Some(v) = state { q.push(("state", v)); } if let Some(v) = labels { q.push(("labels", v)); } if let Some(v) = page { q.push(("page", v.to_string())); } if let Some(v) = limit { q.push(("limit", v.to_string())); } let v = client .get_value(&format!("/repos/{owner}/{name}/issues"), &q) .await?; print_value(fmt, &v); Ok(()) } async fn view(client: &Client, repo: &str, index: u64, fmt: Format) -> Result<()> { let (owner, name) = split_repo(repo)?; let v = client .get_value(&format!("/repos/{owner}/{name}/issues/{index}"), &[]) .await?; print_value(fmt, &v); Ok(()) } async fn labels(client: &Client, repo: &str, index: u64, fmt: Format) -> Result<()> { let (owner, name) = split_repo(repo)?; let v = client .get_value(&format!("/repos/{owner}/{name}/issues/{index}/labels"), &[]) .await?; print_value(fmt, &v); Ok(()) } async fn timeline( client: &Client, repo: &str, index: u64, page: Option<u32>, limit: Option<u32>, fmt: Format, ) -> Result<()> { let (owner, name) = split_repo(repo)?; let mut q: Vec<(&str, String)> = Vec::new(); if let Some(v) = page { q.push(("page", v.to_string())); } if let Some(v) = limit { q.push(("limit", v.to_string())); } let v = client .get_value( &format!("/repos/{owner}/{name}/issues/{index}/timeline"), &q, ) .await?; print_value(fmt, &v); Ok(()) } async fn comment(client: &Client, cmd: CommentCmd, fmt: Format) -> Result<()> { match cmd { CommentCmd::List { repo, index, page, limit, } => { let (owner, name) = split_repo(&repo)?; let mut q: Vec<(&str, String)> = Vec::new(); if let Some(v) = page { q.push(("page", v.to_string())); } if let Some(v) = limit { q.push(("limit", v.to_string())); } let v = client .get_value( &format!("/repos/{owner}/{name}/issues/{index}/comments"), &q, ) .await?; print_value(fmt, &v); Ok(()) } CommentCmd::View { repo, id } => { let (owner, name) = split_repo(&repo)?; let v = client .get_value(&format!("/repos/{owner}/{name}/issues/comments/{id}"), &[]) .await?; print_value(fmt, &v); Ok(()) } CommentCmd::Create { repo, index, body } => { let (owner, name) = split_repo(&repo)?; let mut payload = Map::new(); payload.insert( "body".into(), Value::String(crate::api::resolve_string(body).await?), ); let v = client .send_json( Method::POST, &format!("/repos/{owner}/{name}/issues/{index}/comments"), &Value::Object(payload), ) .await?; print_value(fmt, &v); Ok(()) } CommentCmd::Edit { repo, index, id, body, } => { let (owner, name) = split_repo(&repo)?; let mut payload = Map::new(); payload.insert( "body".into(), Value::String(crate::api::resolve_string(body).await?), ); let v = client .send_json( Method::PATCH, &format!("/repos/{owner}/{name}/issues/{index}/comments/{id}"), &Value::Object(payload), ) .await?; print_value(fmt, &v); Ok(()) } CommentCmd::Delete { repo, index, id, yes, } => { let (owner, name) = split_repo(&repo)?; if !yes { return Err(Error::Invalid(format!( "refusing to delete comment {id} on `{owner}/{name}#{index}` without --yes" ))); } client .send_empty( Method::DELETE, &format!("/repos/{owner}/{name}/issues/{index}/comments/{id}"), ) .await?; eprintln!("deleted comment {id} on `{owner}/{name}#{index}`"); Ok(()) } } } async fn reaction(client: &Client, cmd: ReactionCmd, fmt: Format) -> Result<()> { match cmd { ReactionCmd::Add { repo, index, comment_id, content, } => { let (owner, name) = split_repo(&repo)?; let mut payload = Map::new(); payload.insert("content".into(), Value::String(content)); let v = client .send_json( Method::POST, &format!( "/repos/{owner}/{name}/issues/{index}/comments/{comment_id}/reactions" ), &Value::Object(payload), ) .await?; print_value(fmt, &v); Ok(()) } ReactionCmd::Remove { repo, index, comment_id, reaction_id, yes, } => { let (owner, name) = split_repo(&repo)?; if !yes { return Err(Error::Invalid(format!( "refusing to remove reaction {reaction_id} without --yes" ))); } client .send_empty( Method::DELETE, &format!( "/repos/{owner}/{name}/issues/{index}/comments/{comment_id}/reactions/{reaction_id}" ), ) .await?; eprintln!("removed reaction {reaction_id}"); Ok(()) } } }