use crate::argv::Argv;
#[derive(Clone, Debug)]
pub struct ArgvBorrowed<'a> {
input: &'a [u8],
ranges: Vec<(u32, u32)>,
}
impl<'a> ArgvBorrowed<'a> {
pub fn new(input: &'a [u8]) -> Self {
Self {
input,
ranges: Vec::new(),
}
}
pub fn with_capacity(input: &'a [u8], argc: usize) -> Self {
Self {
input,
ranges: Vec::with_capacity(argc),
}
}
pub(crate) fn push_range(&mut self, start: usize, end: usize) {
debug_assert!(end <= self.input.len() && start <= end);
self.ranges.push((start as u32, end as u32));
}
pub fn len(&self) -> usize {
self.ranges.len()
}
pub fn is_empty(&self) -> bool {
self.ranges.is_empty()
}
pub fn get(&self, i: usize) -> Option<&[u8]> {
let (s, e) = *self.ranges.get(i)?;
Some(&self.input[s as usize..e as usize])
}
pub fn first(&self) -> Option<&[u8]> {
self.get(0)
}
pub fn iter(&self) -> impl Iterator<Item = &[u8]> {
(0..self.len()).map(move |i| self.get(i).expect("in range"))
}
pub fn into_owned(self) -> Argv {
let total: usize = self
.ranges
.iter()
.map(|&(s, e)| (e - s) as usize)
.sum();
let mut a = Argv::with_capacity(self.ranges.len(), total);
for &(s, e) in &self.ranges {
a.push(&self.input[s as usize..e as usize]);
}
a
}
}
impl core::ops::Index<usize> for ArgvBorrowed<'_> {
type Output = [u8];
fn index(&self, i: usize) -> &[u8] {
self.get(i).expect("argv-borrowed index out of bounds")
}
}
impl PartialEq<Vec<Vec<u8>>> for ArgvBorrowed<'_> {
fn eq(&self, other: &Vec<Vec<u8>>) -> bool {
self.len() == other.len() && self.iter().zip(other).all(|(a, b)| a == b.as_slice())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_and_with_capacity_start_empty() {
let buf = b"hello";
let a = ArgvBorrowed::new(buf);
assert!(a.is_empty());
assert_eq!(a.len(), 0);
let b = ArgvBorrowed::with_capacity(buf, 8);
assert!(b.is_empty());
assert!(b.ranges.capacity() >= 8);
}
#[test]
fn push_range_and_get_round_trip() {
let buf: &[u8] = b"*2\r\n$3\r\nGET\r\n$5\r\nmykey\r\n";
let mut a = ArgvBorrowed::with_capacity(buf, 2);
a.push_range(8, 11);
a.push_range(17, 22);
assert_eq!(a.len(), 2);
assert_eq!(a.first(), Some(b"GET" as &[u8]));
assert_eq!(a.get(0), Some(b"GET" as &[u8]));
assert_eq!(a.get(1), Some(b"mykey" as &[u8]));
assert_eq!(a.get(2), None);
}
#[test]
fn iter_yields_args_in_order() {
let buf: &[u8] = b"abcXYZdef";
let mut a = ArgvBorrowed::new(buf);
a.push_range(0, 3);
a.push_range(3, 6);
a.push_range(6, 9);
let collected: Vec<&[u8]> = a.iter().collect();
assert_eq!(collected, vec![b"abc" as &[u8], b"XYZ", b"def"]);
}
#[test]
fn first_empty_returns_none() {
let a = ArgvBorrowed::new(b"" as &[u8]);
assert_eq!(a.first(), None);
assert_eq!(a.get(0), None);
}
#[test]
fn index_returns_correct_slice() {
let buf: &[u8] = b"hithere";
let mut a = ArgvBorrowed::new(buf);
a.push_range(0, 2);
a.push_range(2, 7);
assert_eq!(&a[0], b"hi" as &[u8]);
assert_eq!(&a[1], b"there" as &[u8]);
}
#[test]
#[should_panic(expected = "argv-borrowed index out of bounds")]
fn index_out_of_bounds_panics() {
let a = ArgvBorrowed::new(b"" as &[u8]);
let _ = &a[0];
}
#[test]
fn eq_against_vec_of_vec() {
let buf: &[u8] = b"PINGhello";
let mut a = ArgvBorrowed::new(buf);
a.push_range(0, 4);
a.push_range(4, 9);
assert_eq!(a, vec![b"PING".to_vec(), b"hello".to_vec()]);
assert_ne!(a, vec![b"PING".to_vec()]);
assert_ne!(a, vec![b"PING".to_vec(), b"world".to_vec()]);
}
#[test]
fn into_owned_copies_args_into_argv() {
let buf: &[u8] = b"*2\r\n$3\r\nSET\r\n$1\r\nk\r\n";
let mut a = ArgvBorrowed::with_capacity(buf, 2);
a.push_range(8, 11); a.push_range(17, 18); let owned: Argv = a.into_owned();
assert_eq!(owned.len(), 2);
assert_eq!(owned.get(0), Some(b"SET" as &[u8]));
assert_eq!(owned.get(1), Some(b"k" as &[u8]));
assert_eq!(owned, vec![b"SET".to_vec(), b"k".to_vec()]);
}
#[test]
fn into_owned_on_empty_argv_returns_empty_argv() {
let a = ArgvBorrowed::new(b"" as &[u8]);
let owned = a.into_owned();
assert!(owned.is_empty());
assert_eq!(owned.len(), 0);
}
#[test]
fn clone_shares_input_slice_independent_ranges() {
let buf: &[u8] = b"abcdef";
let mut a = ArgvBorrowed::new(buf);
a.push_range(0, 3); let b = a.clone();
assert_eq!(b.len(), 1);
assert_eq!(b.get(0), Some(b"abc" as &[u8]));
a.push_range(3, 6); assert_eq!(a.len(), 2);
assert_eq!(b.len(), 1);
}
}