codeberg_cli/actions/pull_request/
view.rs1use super::display_pull_request;
2use crate::actions::{text_manipulation::select_prompt_for, GeneralArgs};
3use crate::render::comment::render_comment;
4use crate::render::datetime::render_datetime_and_info;
5use crate::render::option::option_display;
6use crate::render::spinner::spin_until_ready;
7use crate::render::ui::fuzzy_select_with_key;
8use crate::types::api::state_type::ViewStateType;
9use crate::types::context::BergContext;
10use crate::types::git::OwnerRepo;
11use anyhow::Context;
12use clap::Parser;
13use forgejo_api::structs::{
14 IssueGetCommentsQuery, PullRequest, RepoListPullRequestsQuery, RepoListPullRequestsQueryState,
15};
16use itertools::Itertools;
17
18#[derive(Parser, Debug)]
19#[command(about = "View details of a selected pull request")]
20pub struct ViewPullRequestsArgs {
21 #[arg(
23 short,
24 long,
25 value_enum,
26 default_value_t = ViewStateType::All,
27 )]
28 pub state: ViewStateType,
29
30 #[arg(short, long)]
32 pub comments: bool,
33}
34
35impl ViewPullRequestsArgs {
36 pub async fn run(self, general_args: GeneralArgs) -> anyhow::Result<()> {
37 let _ = general_args;
38 let ctx = BergContext::new(self, general_args).await?;
39
40 let OwnerRepo { repo, owner } = ctx.owner_repo()?;
41 let state = match ctx.args.state {
42 ViewStateType::Closed => RepoListPullRequestsQueryState::Closed,
43 ViewStateType::Open => RepoListPullRequestsQueryState::Open,
44 ViewStateType::All => RepoListPullRequestsQueryState::All,
45 };
46 let pull_requests_list = spin_until_ready(ctx.client.repo_list_pull_requests(
47 owner.as_str(),
48 repo.as_str(),
49 RepoListPullRequestsQuery {
50 state: Some(state),
51 ..Default::default()
52 },
53 ))
54 .await?;
55
56 let pull_request = fuzzy_select_with_key(
57 &pull_requests_list,
58 select_prompt_for("pull request"),
59 display_pull_request,
60 )?;
61
62 if ctx.args.comments {
63 spin_until_ready(present_pull_request_comments(&ctx, pull_request)).await?;
64 } else {
65 present_pull_request_overview(&ctx, pull_request);
66 }
67
68 Ok(())
69 }
70}
71
72fn present_pull_request_overview(
73 ctx: &BergContext<ViewPullRequestsArgs>,
74 pull_request: &PullRequest,
75) {
76 let rendered_datetime = pull_request
77 .created_at
78 .as_ref()
79 .map(render_datetime_and_info)
80 .unwrap_or_else(|| String::from("?"));
81
82 let mut table = ctx.make_table();
83
84 table
85 .set_header(vec![format!(
86 "Pull Request #{}",
87 option_display(&pull_request.id)
88 )])
89 .add_row(vec![
90 String::from("Title"),
91 option_display(&pull_request.title),
92 ])
93 .add_row(vec![String::from("Created"), rendered_datetime])
94 .add_row(vec![
95 String::from("Labels"),
96 pull_request
97 .labels
98 .iter()
99 .flatten()
100 .map(|label| option_display(&label.name))
101 .join(", "),
102 ])
103 .add_row(vec![
104 String::from("Description"),
105 option_display(&pull_request.body),
106 ]);
107
108 println!("{table}", table = table.show());
109}
110
111async fn present_pull_request_comments(
112 ctx: &BergContext<ViewPullRequestsArgs>,
113 pull_request: &PullRequest,
114) -> anyhow::Result<()> {
115 let OwnerRepo { repo, owner } = ctx.owner_repo()?;
116 let nr = pull_request.id.context("Selected pull request has no ID")?;
117 let comments = ctx
118 .client
119 .issue_get_comments(
120 owner.as_str(),
121 repo.as_str(),
122 nr as u64,
123 IssueGetCommentsQuery::default(),
124 )
125 .await?;
126 let header = format!(
127 "Pull Request #{} {}",
128 option_display(&pull_request.id),
129 if comments.is_empty() {
130 "(no comments)"
131 } else {
132 "comments"
133 }
134 );
135
136 let mut table = ctx.make_table();
137
138 table
139 .set_header(vec![header])
140 .add_rows(comments.into_iter().filter_map(|comment| {
141 let username = comment.user.as_ref()?.login.as_ref()?.as_str();
142 let creation_time = comment.created_at.as_ref()?;
143 let comment = comment.body.as_ref()?;
144 let comment = render_comment(&ctx.config, username, creation_time, comment);
145 Some(vec![comment])
146 }));
147
148 println!("{table}", table = table.show());
149
150 Ok(())
151}