Skip to main content

codeberg_cli/actions/pull_request/
view.rs

1use 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    /// Optionally, specify a selected issue in the command line
24    pub index: Option<i64>,
25
26    /// Select from pull requests with the chosen state
27    #[arg(
28        short,
29        long,
30        value_enum,
31        default_value_t = ViewStateType::All,
32    )]
33    pub state: ViewStateType,
34
35    /// Disabled: display issue summary | Enabled: display issue comment history
36    #[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}