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