feat: resolve review comments through the CLI

This commit is contained in:
2026-10-24 01:55:35 +02:00
parent 6b04b6f0ac
commit c88582f2ba
8 changed files with 254 additions and 23 deletions
+2 -1
View File
@@ -13,8 +13,9 @@ Usage: vamp [--config PATH | --no-config] [FILE]
vamp [--config PATH | --no-config] --review list [--repo PATH] [--json]
vamp [--config PATH | --no-config] --review show ID [--repo PATH] [--json] [--all]
vamp [--config PATH | --no-config] --review delete REVIEW_ID COMMENT_ID [--repo PATH] [--json]
vamp [--config PATH | --no-config] --review resolve REVIEW_ID COMMENT_ID [--repo PATH] [--json]
Review CLI: list/show are read-only; delete commits one comment deletion immediately.
Review CLI: list/show are read-only; delete/resolve commit one comment change immediately.
Show defaults to unresolved comments; --all includes resolved.
Use vamp -- --review to edit a file named --review.
+49 -19
View File
@@ -9,9 +9,14 @@ use vamp::{
ruby,
};
enum CommentAction {
Delete,
Resolve,
}
pub struct Request {
id: Option<i64>,
delete_comment: Option<u64>,
comment: Option<(CommentAction, u64)>,
repo: Option<PathBuf>,
json: bool,
all: bool,
@@ -19,15 +24,17 @@ pub struct Request {
impl Request {
pub fn parse(args: &mut impl Iterator<Item = OsString>) -> Result<Self, String> {
let usage = "--review requires list, show ID, or delete REVIEW_ID COMMENT_ID";
let usage = "--review requires list, show ID, delete REVIEW_ID COMMENT_ID, or resolve REVIEW_ID COMMENT_ID";
let command = args.next().ok_or(usage)?;
let id = if command == "list" {
None
} else if command == "show" || command == "delete" {
} else if command == "show" || command == "delete" || command == "resolve" {
let error = if command == "show" {
"`show` requires a positive review ID.\nUsage: vamp --review show ID [--all]\n`--all` includes resolved comments; it does not select all reviews."
} else {
} else if command == "delete" {
"`delete` requires a positive review ID.\nUsage: vamp --review delete REVIEW_ID COMMENT_ID"
} else {
"`resolve` requires a positive review ID.\nUsage: vamp --review resolve REVIEW_ID COMMENT_ID"
};
let id = args.next().ok_or(error)?;
Some(
@@ -39,21 +46,32 @@ impl Request {
} else {
return Err(usage.into());
};
let delete_comment = if command == "delete" {
let error = "delete requires a positive comment ID";
let comment = if command == "delete" || command == "resolve" {
let error = if command == "delete" {
"delete requires a positive comment ID"
} else {
"resolve requires a positive comment ID"
};
let id = args.next().ok_or(error)?;
Some(
id.to_str()
.and_then(|id| id.parse::<u64>().ok())
.filter(|id| *id > 0)
.ok_or(error)?,
)
let id = id
.to_str()
.and_then(|id| id.parse::<u64>().ok())
.filter(|id| *id > 0)
.ok_or(error)?;
Some((
if command == "delete" {
CommentAction::Delete
} else {
CommentAction::Resolve
},
id,
))
} else {
None
};
Ok(Self {
id,
delete_comment,
comment,
repo: None,
json: false,
all: false,
@@ -69,8 +87,7 @@ impl Request {
self.repo = Some(PathBuf::from(args.next().ok_or("--repo requires a path")?));
} else if arg == "--json" && !self.json {
self.json = true;
} else if arg == "--all" && self.id.is_some() && self.delete_comment.is_none() && !self.all
{
} else if arg == "--all" && self.id.is_some() && self.comment.is_none() && !self.all {
self.all = true;
} else {
return Err(format!(
@@ -124,14 +141,27 @@ pub fn run(request: &Request, config: Option<&Path>) -> Result<(), String> {
root.display()
))?
.load(&root, id)?;
if let Some(comment_id) = request.delete_comment {
if let Some((action, comment_id)) = &request.comment {
let index = review
.comments
.iter()
.position(|comment| comment.id == comment_id)
.position(|comment| comment.id == *comment_id)
.ok_or_else(|| format!("Comment {comment_id} does not exist in review {id}"))?;
review.comments.remove(index);
Store::open(&directory)?.save(&mut review)?;
let changed = match action {
CommentAction::Delete => {
review.comments.remove(index);
true
}
CommentAction::Resolve => {
let comment = &mut review.comments[index];
let changed = !comment.resolved;
comment.resolved = true;
changed
}
};
if changed {
Store::open(&directory)?.save(&mut review)?;
}
}
vec![review]
} else {