1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
//! Bug subcommand handlers, split per-action.
use crate::cli::BugAction;
use crate::error::Result;
use crate::output::resources::bug::{
validate_json_field_selection, validate_table_columns, warn_unknown_fields, ColumnSpec,
};
use crate::output::writers::Writers;
use crate::types::{ApiMode, OutputFormat};
mod clone;
mod create;
mod history;
mod list;
mod my;
mod search;
mod update;
mod view;
/// The `--fields` / `--exclude-fields` column spec for the four field-bearing
/// bug actions, or `None` for actions that take no field selection.
fn bug_column_spec(action: &BugAction) -> Option<ColumnSpec<'_>> {
match action {
BugAction::List {
fields,
exclude_fields,
..
}
| BugAction::My {
fields,
exclude_fields,
..
}
| BugAction::Search {
fields,
exclude_fields,
..
}
| BugAction::View {
fields,
exclude_fields,
..
} => Some(ColumnSpec {
include: fields.as_deref(),
exclude: exclude_fields.as_deref(),
}),
_ => None,
}
}
/// Dispatch bug actions to their respective handlers.
pub async fn execute(
action: &BugAction,
server: Option<&str>,
format: OutputFormat,
api: Option<ApiMode>,
w: &mut Writers<'_>,
) -> Result<()> {
update::validate_action(action)?;
// Resolve the field selection before any network I/O. A selection that
// leaves nothing to show exits 7 deterministically — measured against the
// five-column default in table mode, against the full field universe in
// JSON mode (where output is trimmed gh-style to the selected keys). Either
// way the exit code is independent of subcommand or server reachability.
// `bug view` is exempt from this zero-field error in both modes: a sparse
// (or empty `{}`) single-bug result is coherent, not an error. Unknown
// `--fields` tokens (typos, custom `cf_*` fields) warn once on stderr;
// under JSON the warning fires for every action including `view`, since a
// typo would otherwise silently yield `{}`.
if let Some(spec) = bug_column_spec(action) {
let is_view = matches!(action, BugAction::View { .. });
match format {
OutputFormat::Table => {
if !is_view {
validate_table_columns(spec)?;
}
}
OutputFormat::Json => {
if !is_view {
validate_json_field_selection(spec)?;
}
warn_unknown_fields(spec, w.err);
}
}
}
// Search builds its own client because --from-url may resolve a different
// server from the URL hostname. Skip the shared connect to avoid double
// auth/version detection on every `bug search` invocation.
if let BugAction::Search { .. } = action {
return search::handle(action, server, format, api, w).await;
}
let client = crate::commands::shared::connect_and_configure(server, api).await?;
match action {
BugAction::List { .. } => list::handle(&client, action, format, w).await,
BugAction::View { .. } => view::handle(&client, action, format, w).await,
BugAction::History { .. } => history::handle(&client, action, format, w).await,
BugAction::My { .. } => my::handle(&client, action, format, w).await,
BugAction::Create { .. } => create::handle(&client, action, format, w).await,
BugAction::Clone { .. } => clone::handle(&client, action, format, w).await,
BugAction::Update { .. } => update::handle(&client, action, format, w).await,
BugAction::Search { .. } => unreachable!("handled above"),
}
}