use kevy_resp::{ArgvView, encode_error, parse_command};
use kevy_store::Store;
use crate::state::Ctx;
pub(crate) fn cmd_move_scope_ingest<A: ArgvView + ?Sized>(
ctx: &Ctx<'_>,
store: &mut Store,
args: &A,
out: &mut Vec<u8>,
) {
if args.len() != 3 {
return encode_error(
out,
"ERR wrong number of arguments — MOVE-SCOPE-INGEST <prefix> <bulk>",
);
}
let Some(prefix) = args.get(1) else {
return encode_error(out, "ERR MOVE-SCOPE-INGEST: missing prefix");
};
let Some(bulk) = args.get(2) else {
return encode_error(out, "ERR MOVE-SCOPE-INGEST: missing bulk");
};
let _guard = ctx.shard.ingest_guard(prefix.to_vec());
let applied = match apply_ingest_frames(ctx, store, bulk) {
Ok(n) => n,
Err(IngestError::Malformed) => {
return encode_error(out, "ERR MOVE-SCOPE-INGEST: malformed bulk");
}
Err(IngestError::Refused(why)) => {
return encode_error(out, &format!("ERR MOVE-SCOPE-INGEST: {why}"));
}
};
let reply = format!("+OK {applied}\r\n");
out.extend_from_slice(reply.as_bytes());
}
pub(crate) enum IngestError {
Malformed,
Refused(String),
}
fn apply_ingest_frames(
ctx: &Ctx<'_>,
store: &mut Store,
bulk: &[u8],
) -> Result<usize, IngestError> {
let mut buf = bulk.to_vec();
let mut applied = 0usize;
let mut scratch = Vec::with_capacity(256);
loop {
match parse_command(&buf) {
Ok(Some((argv, consumed))) => {
scratch.clear();
crate::dispatch::dispatch_into(ctx, store, &argv, &mut scratch);
if scratch.first() == Some(&b'-') {
return Err(IngestError::Refused(refusal_detail(&argv, &scratch)));
}
if let Some(key) = argv.get(1) {
note_ingested_key(ctx, store, key);
}
buf.drain(..consumed);
applied += 1;
}
Ok(None) => return Ok(applied),
Err(_) => return Err(IngestError::Malformed),
}
}
}
fn refusal_detail(argv: &kevy_resp::Argv, reply: &[u8]) -> String {
let key = argv.get(1).map(|k| String::from_utf8_lossy(k).into_owned());
let msg = String::from_utf8_lossy(reply);
let msg = msg.trim_start_matches('-').trim_end().to_string();
match key {
Some(k) => format!("key '{k}' refused by this node: {msg}"),
None => format!("a frame was refused by this node: {msg}"),
}
}
fn note_ingested_key(ctx: &Ctx<'_>, store: &mut Store, key: &[u8]) {
if ctx.state.catalogs.index_nonempty() {
crate::index_runtime::on_write(ctx, store, key);
}
if ctx.state.catalogs.view_nonempty() {
crate::view_runtime::on_write(ctx, store, key);
}
}