Skip to content

Commit 5d434de

Browse files
committed
refactor: split feedback command helpers
Separate feedback file loading and ID normalization from store persistence so command-side wiring stays distinct from mutation logic. Made-with: Cursor
1 parent c25bdb2 commit 5d434de

File tree

4 files changed

+111
-39
lines changed

4 files changed

+111
-39
lines changed

TODO.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@
8585
- [x] `src/commands/pr/gh.rs`: carve PR resolution, diff fetching, and metadata fetching.
8686
- [x] `src/commands/git/suggest.rs`: split commit-message prompting from PR-title prompting and response extraction.
8787
- [x] `src/commands/review/command.rs`: split review/check/compare entrypoints if they keep diverging.
88-
- [ ] `src/commands/misc/feedback/command.rs`: separate file loading/ID normalization from store persistence.
88+
- [x] `src/commands/misc/feedback/command.rs`: separate file loading/ID normalization from store persistence.
8989
- [ ] `src/commands/misc/feedback/apply.rs`: split acceptance/rejection counters from store mutation helpers.
9090
- [ ] `src/commands/misc/discussion/command.rs`: separate the interactive loop from single-shot execution.
9191
- [ ] `src/commands/misc/discussion/selection.rs`: split file loading/ID repair from selection rules.
Lines changed: 23 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,45 @@
1+
#[path = "command/input.rs"]
2+
mod input;
3+
#[path = "command/store.rs"]
4+
mod store;
5+
16
use anyhow::Result;
27
use std::path::PathBuf;
38

49
use crate::config;
5-
use crate::core;
610
use crate::review;
711

8-
use super::apply::{apply_feedback_accept, apply_feedback_reject};
912
use super::conventions::record_convention_feedback;
13+
use input::{load_feedback_command_input, FeedbackAction};
14+
use store::apply_feedback_store_update;
1015

1116
pub async fn feedback_command(
1217
mut config: config::Config,
1318
accept: Option<PathBuf>,
1419
reject: Option<PathBuf>,
1520
feedback_path: Option<PathBuf>,
1621
) -> Result<()> {
17-
let (action, input_path) = match (accept, reject) {
18-
(Some(path), None) => ("accept", path),
19-
(None, Some(path)) => ("reject", path),
20-
_ => {
21-
anyhow::bail!("Specify exactly one of --accept or --reject");
22-
}
23-
};
24-
25-
let feedback_path = feedback_path.unwrap_or_else(|| config.feedback_path.clone());
26-
config.feedback_path = feedback_path.clone();
27-
let content = tokio::fs::read_to_string(&input_path).await?;
28-
let mut comments: Vec<core::Comment> = serde_json::from_str(&content)?;
29-
30-
for comment in &mut comments {
31-
if comment.id.trim().is_empty() {
32-
comment.id = core::comment::compute_comment_id(
33-
&comment.file_path,
34-
&comment.content,
35-
&comment.category,
36-
);
37-
}
38-
}
39-
40-
let mut store = review::load_feedback_store_from_path(&feedback_path);
41-
let updated = if action == "accept" {
42-
apply_feedback_accept(&mut store, &comments)
43-
} else {
44-
apply_feedback_reject(&mut store, &comments)
45-
};
46-
47-
review::save_feedback_store(&feedback_path, &store)?;
22+
let command_input = load_feedback_command_input(&config, accept, reject, feedback_path).await?;
23+
config.feedback_path = command_input.feedback_path.clone();
24+
25+
let updated = apply_feedback_store_update(
26+
&command_input.feedback_path,
27+
command_input.action,
28+
&command_input.comments,
29+
)?;
30+
4831
println!(
4932
"Updated feedback store at {} ({} {} comment(s))",
50-
feedback_path.display(),
33+
command_input.feedback_path.display(),
5134
updated,
52-
action
35+
command_input.action.as_str()
5336
);
5437

55-
let is_accepted = action == "accept";
56-
let _ = review::record_semantic_feedback_examples(&config, &comments, is_accepted).await;
57-
record_convention_feedback(&config, &comments, is_accepted);
38+
let is_accepted = matches!(command_input.action, FeedbackAction::Accept);
39+
let _ =
40+
review::record_semantic_feedback_examples(&config, &command_input.comments, is_accepted)
41+
.await;
42+
record_convention_feedback(&config, &command_input.comments, is_accepted);
5843

5944
Ok(())
6045
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
use anyhow::Result;
2+
use std::path::PathBuf;
3+
4+
use crate::config;
5+
use crate::core;
6+
7+
#[derive(Clone, Copy)]
8+
pub(super) enum FeedbackAction {
9+
Accept,
10+
Reject,
11+
}
12+
13+
impl FeedbackAction {
14+
pub(super) fn as_str(&self) -> &'static str {
15+
match self {
16+
Self::Accept => "accept",
17+
Self::Reject => "reject",
18+
}
19+
}
20+
}
21+
22+
pub(super) struct FeedbackCommandInput {
23+
pub(super) action: FeedbackAction,
24+
pub(super) feedback_path: PathBuf,
25+
pub(super) comments: Vec<core::Comment>,
26+
}
27+
28+
pub(super) async fn load_feedback_command_input(
29+
config: &config::Config,
30+
accept: Option<PathBuf>,
31+
reject: Option<PathBuf>,
32+
feedback_path: Option<PathBuf>,
33+
) -> Result<FeedbackCommandInput> {
34+
let (action, input_path) = match (accept, reject) {
35+
(Some(path), None) => (FeedbackAction::Accept, path),
36+
(None, Some(path)) => (FeedbackAction::Reject, path),
37+
_ => {
38+
anyhow::bail!("Specify exactly one of --accept or --reject");
39+
}
40+
};
41+
42+
let feedback_path = feedback_path.unwrap_or_else(|| config.feedback_path.clone());
43+
let content = tokio::fs::read_to_string(&input_path).await?;
44+
let mut comments: Vec<core::Comment> = serde_json::from_str(&content)?;
45+
normalize_comment_ids(&mut comments);
46+
47+
Ok(FeedbackCommandInput {
48+
action,
49+
feedback_path,
50+
comments,
51+
})
52+
}
53+
54+
fn normalize_comment_ids(comments: &mut [core::Comment]) {
55+
for comment in comments {
56+
if comment.id.trim().is_empty() {
57+
comment.id = core::comment::compute_comment_id(
58+
&comment.file_path,
59+
&comment.content,
60+
&comment.category,
61+
);
62+
}
63+
}
64+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
use anyhow::Result;
2+
use std::path::Path;
3+
4+
use crate::core;
5+
use crate::review;
6+
7+
use super::super::apply::{apply_feedback_accept, apply_feedback_reject};
8+
use super::input::FeedbackAction;
9+
10+
pub(super) fn apply_feedback_store_update(
11+
feedback_path: &Path,
12+
action: FeedbackAction,
13+
comments: &[core::Comment],
14+
) -> Result<usize> {
15+
let mut store = review::load_feedback_store_from_path(feedback_path);
16+
let updated = match action {
17+
FeedbackAction::Accept => apply_feedback_accept(&mut store, comments),
18+
FeedbackAction::Reject => apply_feedback_reject(&mut store, comments),
19+
};
20+
21+
review::save_feedback_store(feedback_path, &store)?;
22+
Ok(updated)
23+
}

0 commit comments

Comments
 (0)