Same bug as the swarmctl/hivectl fix, a third instance argus's review didn't name but nix/checks.nix's hive-forge-docs freshness check (same pattern as hivectl-docs/swarmctl-docs) caught in CI: the earlier Contractions/Foreign/Auto batches edited docs/tools/forge-cli.md directly instead of the clap #[arg(...)]/doc-comment strings in hive-forge/src/main.rs and hive-forge/src/verbs/*.rs. Applied the same 13 wording changes to source that the earlier commits already made to the generated .md, matched 1:1 against 'git diff origin/main HEAD -- docs/tools/forge-cli.md' rather than guessed. Several source doc comments feed two rendered sections each (e.g. reaction.rs's one Add-variant doc renders under both 'issue reaction add' and 'pr reaction add', since both subcommands share the same enum) -- one source fix, two generated-doc fixes. Regenerated from the now-fixed source and confirmed byte-identical to what's already committed (diff exit 0) -- source and generated output are back in sync, same as the swarmctl/hivectl fix. cargo clippy -p hive-forge --all-targets -- -D warnings and scripts/check-doc-refs.sh both clean.
113 lines
4.3 KiB
Rust
113 lines
4.3 KiB
Rust
//! `reaction <number> [--comment <id>] [list|add|remove] [content]` —
|
|
//! list, add, or remove emoji reactions on an issue/PR or one of its
|
|
//! comments.
|
|
//!
|
|
//! Forgejo's reaction endpoints work on the shared issue/PR index (PRs
|
|
//! are issues internally, same as `dependency`) when no `--comment` is
|
|
//! given; `--comment <id>` redirects to the separate comment-reaction
|
|
//! endpoint pair, keyed on the comment's own id (not the parent
|
|
//! issue/PR number — `number` still has to be passed so this verb slots
|
|
//! into `issue <n> reaction` / `pr <n> reaction` like every other
|
|
//! generic verb, but it's otherwise unused on that path).
|
|
//!
|
|
//! `content` is Forgejo's reaction shortcode vocabulary (`+1`, `-1`,
|
|
//! `laugh`, `confused`, `heart`, `hooray`, `rocket`, `eyes`, …), not a
|
|
//! raw emoji character — same set the forge web UI's reaction picker
|
|
//! offers. `--list-allowed` prints the *actual* instance-configured
|
|
//! list (`GET /settings/ui`'s `allowed_reactions`) instead of leaving a
|
|
//! caller to guess it or learn it from a 403 — this is instance-global,
|
|
//! not issue/PR-scoped, so `number`/`--comment` are ignored when it's
|
|
//! set (the number is still required positionally, same as every other
|
|
//! generic verb, but unused on this path).
|
|
|
|
use anyhow::Result;
|
|
use clap::{Args as ClapArgs, Subcommand};
|
|
use forgejo_api::structs::EditReactionOption;
|
|
|
|
use crate::client::{Client, index};
|
|
use crate::verbs::{comment_reactions, issue_reactions, print_json};
|
|
|
|
#[derive(ClapArgs)]
|
|
pub struct Args {
|
|
/// Issue or PR number. Ignored (but still required) when
|
|
/// `--list-allowed` is set.
|
|
pub(crate) number: u64,
|
|
/// Target a specific comment's reactions instead of the issue/PR
|
|
/// itself — the comment's own id (from `comments`/`comment-show`),
|
|
/// not its position in the thread.
|
|
#[arg(long)]
|
|
comment: Option<u64>,
|
|
/// Print the instance's actual configured reaction shortcodes
|
|
/// (`GET /settings/ui`) instead of listing/adding/removing —
|
|
/// instance-global, ignores `number`/`--comment`.
|
|
#[arg(long)]
|
|
pub(crate) list_allowed: bool,
|
|
#[command(subcommand)]
|
|
action: Option<Action>,
|
|
}
|
|
|
|
#[derive(Subcommand)]
|
|
enum Action {
|
|
/// List reactions (default when no action is given).
|
|
List,
|
|
/// Add a reaction — a Forgejo shortcode, for example `+1`, `heart`, `rocket`.
|
|
Add { content: String },
|
|
/// Remove your own reaction with this content.
|
|
Remove { content: String },
|
|
}
|
|
|
|
pub fn run(client: &Client, args: Args) -> Result<()> {
|
|
if args.list_allowed {
|
|
let settings = client.api().get_general_ui_settings().send()?;
|
|
return print_json(&serde_json::json!(
|
|
settings.allowed_reactions.unwrap_or_default()
|
|
));
|
|
}
|
|
let (owner, name) = client.owner_repo()?;
|
|
match args.action.unwrap_or(Action::List) {
|
|
Action::List => {}
|
|
Action::Add { content } => {
|
|
let body = EditReactionOption {
|
|
content: Some(content),
|
|
};
|
|
match args.comment {
|
|
Some(id) => {
|
|
client
|
|
.api()
|
|
.issue_post_comment_reaction(owner, name, index(id)?, body)
|
|
.send()?;
|
|
}
|
|
None => {
|
|
client
|
|
.api()
|
|
.issue_post_issue_reaction(owner, name, index(args.number)?, body)
|
|
.send()?;
|
|
}
|
|
}
|
|
}
|
|
Action::Remove { content } => {
|
|
let body = EditReactionOption {
|
|
content: Some(content),
|
|
};
|
|
match args.comment {
|
|
Some(id) => {
|
|
client
|
|
.api()
|
|
.issue_delete_comment_reaction(owner, name, index(id)?, body)
|
|
.send()?;
|
|
}
|
|
None => {
|
|
client
|
|
.api()
|
|
.issue_delete_issue_reaction(owner, name, index(args.number)?, body)
|
|
.send()?;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
let reactions = match args.comment {
|
|
Some(id) => comment_reactions(client, owner, name, id)?,
|
|
None => issue_reactions(client, owner, name, args.number)?,
|
|
};
|
|
print_json(&serde_json::json!(reactions))
|
|
}
|