use crate::ast::*;
use crate::builders::WireContext;
use alloc::vec::Vec;
pub fn build_memcached_payload(
_ctx: &WireContext,
spec: &MemcachedSpec,
payload_bytes: &[u8],
) -> Vec<u8> {
if spec.opcode.is_some() || spec.opaque.is_some() || spec.cas.is_some() {
let cmd_upper = spec.cmd.unwrap_or("GET").to_ascii_uppercase();
let opcode = spec.opcode.unwrap_or(match cmd_upper.as_str() {
"GET" => 0x00,
"SET" => 0x01,
"ADD" => 0x02,
"REPLACE" => 0x03,
"DELETE" => 0x04,
"INCREMENT" | "INCR" => 0x05,
"DECREMENT" | "DECR" => 0x06,
"QUIT" => 0x07,
"FLUSH" | "FLUSH_ALL" => 0x08,
"NOOP" => 0x0A,
"VERSION" => 0x0B,
"APPEND" => 0x0E,
"PREPEND" => 0x0F,
"STAT" | "STATS" => 0x10,
_ => 0x00,
});
let key_bytes = spec
.key
.unwrap_or(if matches!(opcode, 0x07 | 0x08 | 0x0A | 0x0B | 0x10) {
""
} else {
"key"
})
.as_bytes();
let val_bytes: &[u8] = if let Some(v) = spec.value {
v.as_bytes()
} else {
payload_bytes
};
let mut extras = Vec::new();
if matches!(opcode, 0x01..=0x03) || spec.flags.is_some() || spec.exptime.is_some() {
extras.extend_from_slice(&spec.flags.unwrap_or(0).to_be_bytes());
extras.extend_from_slice(&spec.exptime.unwrap_or(0).to_be_bytes());
}
let total_body_len = (extras.len() + key_bytes.len() + val_bytes.len()) as u32;
let mut pkt = Vec::with_capacity(24 + total_body_len as usize);
pkt.push(0x80); pkt.push(opcode);
pkt.extend_from_slice(&(key_bytes.len() as u16).to_be_bytes());
pkt.push(extras.len() as u8);
pkt.push(0x00); pkt.extend_from_slice(&0u16.to_be_bytes()); pkt.extend_from_slice(&total_body_len.to_be_bytes());
pkt.extend_from_slice(&spec.opaque.unwrap_or(0).to_be_bytes());
pkt.extend_from_slice(&spec.cas.unwrap_or(0).to_be_bytes());
pkt.extend_from_slice(&extras);
pkt.extend_from_slice(key_bytes);
pkt.extend_from_slice(val_bytes);
if spec.value.is_some() && !payload_bytes.is_empty() {
pkt.extend_from_slice(payload_bytes);
}
return pkt;
}
let is_storage_fields =
spec.flags.is_some() || spec.exptime.is_some() || spec.value.is_some();
let cmd_lower = spec
.cmd
.unwrap_or(if is_storage_fields { "set" } else { "get" })
.to_ascii_lowercase();
if matches!(
cmd_lower.as_str(),
"set" | "add" | "replace" | "append" | "prepend"
) || is_storage_fields
{
let key = spec.key.unwrap_or("key");
let flags = spec.flags.unwrap_or(0);
let exptime = spec.exptime.unwrap_or(0);
let val_bytes: &[u8] = if let Some(v) = spec.value {
v.as_bytes()
} else {
payload_bytes
};
let mut bytes = format!(
"{} {} {} {} {}\r\n",
cmd_lower,
key,
flags,
exptime,
val_bytes.len()
)
.into_bytes();
bytes.extend_from_slice(val_bytes);
bytes.extend_from_slice(b"\r\n");
if spec.value.is_some() && !payload_bytes.is_empty() {
bytes.extend_from_slice(payload_bytes);
}
return bytes;
}
let mut bytes = if matches!(
cmd_lower.as_str(),
"stats" | "version" | "flush_all" | "quit"
) && spec.key.is_none()
{
format!("{}\r\n", cmd_lower).into_bytes()
} else {
let key = spec.key.unwrap_or("key");
format!("{} {}\r\n", cmd_lower, key).into_bytes()
};
if !payload_bytes.is_empty() {
bytes.extend_from_slice(payload_bytes);
}
bytes
}