use anyhow::{Context, Result};
use rusqlite::OptionalExtension;
use serde::Serialize;
use crate::store::Store;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Feedback {
Up,
Down,
Custom(i64),
}
impl Feedback {
pub fn delta(self) -> i64 {
match self {
Feedback::Up => 1,
Feedback::Down => -1,
Feedback::Custom(n) => n,
}
}
}
pub fn record_feedback(store: &Store, chunk_id: &str, feedback: Feedback) -> Result<i64> {
let delta = feedback.delta();
let conn = store.conn();
let rows = conn
.execute(
"UPDATE chunks SET feedback_score = feedback_score + ?1 WHERE id = ?2",
rusqlite::params![delta, chunk_id],
)
.with_context(|| format!("applying feedback to chunk {chunk_id}"))?;
if rows == 0 {
anyhow::bail!("no chunk with id {chunk_id}");
}
get_feedback_score(store, chunk_id)?
.ok_or_else(|| anyhow::anyhow!("chunk {chunk_id} disappeared after feedback update"))
}
pub fn get_feedback_score(store: &Store, chunk_id: &str) -> Result<Option<i64>> {
let conn = store.conn();
let score = conn
.query_row(
"SELECT feedback_score FROM chunks WHERE id = ?1",
rusqlite::params![chunk_id],
|row| row.get::<_, i64>(0),
)
.optional()?;
Ok(score)
}
#[derive(Debug, Clone, Serialize)]
pub struct FeedbackReport {
pub chunk_id: String,
pub delta: i64,
pub score: i64,
}
pub fn apply_feedback(store: &Store, chunk_id: &str, feedback: Feedback) -> Result<FeedbackReport> {
let delta = feedback.delta();
let score = record_feedback(store, chunk_id, feedback)?;
Ok(FeedbackReport {
chunk_id: chunk_id.to_string(),
delta,
score,
})
}
pub fn print_text(report: &FeedbackReport) {
println!(
"feedback recorded: chunk={} delta={:+} score={}",
report.chunk_id, report.delta, report.score
);
}
pub fn print_json(report: &FeedbackReport) -> Result<()> {
println!("{}", serde_json::to_string_pretty(report)?);
Ok(())
}