codeberg_cli/actions/pull_request/
view.rs

1use 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    /// Select from pull requests with the chosen state
22    #[arg(
23        short,
24        long,
25        value_enum,
26        default_value_t = ViewStateType::All,
27    )]
28    pub state: ViewStateType,
29
30    /// Disabled: display issue summary | Enabled: display issue comment history
31    #[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}