diff options
author | Cyborus <cyborus@cyborus.xyz> | 2024-05-01 00:45:43 +0200 |
---|---|---|
committer | Cyborus <cyborus@cyborus.xyz> | 2024-05-07 21:24:57 +0200 |
commit | 6fd197601337227c7e2b6ae54fe98dd951e01eca (patch) | |
tree | fff52b565b9e992f336319d10d3666d3bf21f95b /src/prs.rs | |
parent | add pr commands (diff) | |
download | forgejo-cli-6fd197601337227c7e2b6ae54fe98dd951e01eca.tar.xz forgejo-cli-6fd197601337227c7e2b6ae54fe98dd951e01eca.zip |
add pr change viewing commands
Diffstat (limited to 'src/prs.rs')
-rw-r--r-- | src/prs.rs | 173 |
1 files changed, 171 insertions, 2 deletions
@@ -1,7 +1,10 @@ use clap::{Args, Subcommand}; use eyre::OptionExt; use forgejo_api::{ - structs::{CreatePullRequestOption, MergePullRequestOption}, + structs::{ + CreatePullRequestOption, MergePullRequestOption, RepoGetPullRequestCommitsQuery, + RepoGetPullRequestFilesQuery, + }, Forgejo, }; @@ -107,8 +110,24 @@ pub enum EditCommand { #[derive(Subcommand, Clone, Debug)] pub enum ViewCommand { Body, - Comment { idx: usize }, + Comment { + idx: usize, + }, Comments, + Diff { + /// Get the diff in patch format + #[clap(long, short)] + patch: bool, + /// View the diff in your text editor + #[clap(long, short)] + editor: bool, + }, + Files, + Commits { + /// View one commit per line + #[clap(long, short)] + oneline: bool, + }, } impl PrCommand { @@ -133,6 +152,13 @@ impl PrCommand { crate::issues::view_comment(&repo, &api, id, idx).await? } ViewCommand::Comments => crate::issues::view_comments(&repo, &api, id).await?, + ViewCommand::Diff { patch, editor } => { + view_diff(&repo, &api, id, patch, editor).await? + } + ViewCommand::Files => view_pr_files(&repo, &api, id).await?, + ViewCommand::Commits { oneline } => { + view_pr_commits(&repo, &api, id, oneline).await? + } }, Search { query, @@ -308,3 +334,146 @@ async fn view_prs( } Ok(()) } + +async fn view_diff( + repo: &RepoName, + api: &Forgejo, + pr: u64, + patch: bool, + editor: bool, +) -> eyre::Result<()> { + let diff_type = if patch { "patch" } else { "diff" }; + let diff = api + .repo_download_pull_diff_or_patch( + repo.owner(), + repo.name(), + pr, + diff_type, + forgejo_api::structs::RepoDownloadPullDiffOrPatchQuery::default(), + ) + .await?; + if editor { + let mut view = diff.clone(); + crate::editor(&mut view, Some(diff_type)).await?; + if view != diff { + println!("changes made to the diff will not persist"); + } + } else { + println!("{diff}"); + } + Ok(()) +} + +async fn view_pr_files(repo: &RepoName, api: &Forgejo, pr: u64) -> eyre::Result<()> { + let query = RepoGetPullRequestFilesQuery { + limit: Some(u32::MAX), + ..Default::default() + }; + let (headers, files) = api + .repo_get_pull_request_files(repo.owner(), repo.name(), pr, query) + .await?; + let max_additions = files + .iter() + .map(|x| x.additions.unwrap_or_default()) + .max() + .unwrap_or_default(); + let max_deletions = files + .iter() + .map(|x| x.deletions.unwrap_or_default()) + .max() + .unwrap_or_default(); + + let additions_width = max_additions.checked_ilog10().unwrap_or_default() as usize + 1; + let deletions_width = max_deletions.checked_ilog10().unwrap_or_default() as usize + 1; + + for file in files { + let name = file.filename.as_deref().unwrap_or("???"); + let additions = file.additions.unwrap_or_default(); + let deletions = file.deletions.unwrap_or_default(); + println!("\x1b[92m+{additions:<additions_width$} \x1b[91m-{deletions:<deletions_width$}\x1b[0m {name}"); + } + Ok(()) +} + +async fn view_pr_commits( + repo: &RepoName, + api: &Forgejo, + pr: u64, + oneline: bool, +) -> eyre::Result<()> { + let query = RepoGetPullRequestCommitsQuery { + limit: Some(u32::MAX), + files: Some(false), + ..Default::default() + }; + let (_headers, commits) = api + .repo_get_pull_request_commits(repo.owner(), repo.name(), pr, query) + .await?; + + let max_additions = commits + .iter() + .filter_map(|x| x.stats.as_ref()) + .map(|x| x.additions.unwrap_or_default()) + .max() + .unwrap_or_default(); + let max_deletions = commits + .iter() + .filter_map(|x| x.stats.as_ref()) + .map(|x| x.deletions.unwrap_or_default()) + .max() + .unwrap_or_default(); + + let additions_width = max_additions.checked_ilog10().unwrap_or_default() as usize + 1; + let deletions_width = max_deletions.checked_ilog10().unwrap_or_default() as usize + 1; + + for commit in commits { + let repo_commit = commit + .commit + .as_ref() + .ok_or_eyre("commit does not have commit?")?; + + let message = repo_commit.message.as_deref().unwrap_or("[no msg]"); + let name = message.lines().next().unwrap_or(&message); + + let sha = commit + .sha + .as_deref() + .ok_or_eyre("commit does not have sha")?; + let short_sha = &sha[..7]; + + let stats = commit + .stats + .as_ref() + .ok_or_eyre("commit does not have stats")?; + let additions = stats.additions.unwrap_or_default(); + let deletions = stats.deletions.unwrap_or_default(); + + if oneline { + println!("\x1b[33m{short_sha}\x1b[0m \x1b[92m+{additions:<additions_width$} \x1b[91m-{deletions:<deletions_width$}\x1b[0m {name}"); + } else { + let author = repo_commit + .author + .as_ref() + .ok_or_eyre("commit has no author")?; + let author_name = author.name.as_deref().ok_or_eyre("author has no name")?; + let author_email = author.email.as_deref().ok_or_eyre("author has no email")?; + let date = commit + .created + .as_ref() + .ok_or_eyre("commit as no creation date")?; + + println!("\x1b[33mcommit {sha}\x1b[0m (\x1b[92m+{additions}\x1b[0m, \x1b[91m-{deletions}\x1b[0m)"); + println!("Author: {author_name} <{author_email}>"); + print!("Date: "); + let format = time::macros::format_description!("[weekday repr:short] [month repr:short] [day] [hour repr:24]:[minute]:[second] [year] [offset_hour sign:mandatory][offset_minute]"); + date.format_into(&mut std::io::stdout().lock(), format)?; + println!(); + println!(); + for line in message.lines() { + println!(" {line}"); + } + println!(); + } + } + Ok(()) +} |