Skip to main content

kevy_resp/
argv_view.rs

1//! A read-only argv abstraction shared by [`crate::Argv`] (owned) and
2//! [`crate::ArgvBorrowed`] (zero-copy view into a buffer).
3//!
4//! `ArgvView` is the trait the command runtime takes by generic — every verb
5//! and routing decision works against this trait so the reactor's local
6//! single-shard hot path can dispatch directly from a borrowed argv, while
7//! cross-shard / MULTI queue / AOF logging materialise an owned `Argv` at the
8//! handoff juncture via `into_owned()`.
9//!
10//! Index access (`args[i]`) is part of the contract via the `Index<usize,
11//! Output = [u8]>` supertrait, so verb implementations keep the existing
12//! `args[i]` / `args.iter()` / `args.first()` syntax across the switch.
13
14use crate::argv::Argv;
15use crate::argv_borrowed::ArgvBorrowed;
16
17/// Read-only view over a parsed command's argument vector.
18///
19/// Implemented by both [`Argv`] (owned) and [`ArgvBorrowed`] (zero-copy). The
20/// command runtime takes argvs as `&impl ArgvView`, so the local fast path
21/// can hand a borrowed argv straight to dispatch with no memcpy.
22pub trait ArgvView: core::ops::Index<usize, Output = [u8]> {
23    /// Number of arguments.
24    fn len(&self) -> usize;
25    /// Argument `i` as a byte slice, or `None` if out of range.
26    fn get(&self, i: usize) -> Option<&[u8]>;
27
28    /// Whether there are no arguments.
29    fn is_empty(&self) -> bool {
30        self.len() == 0
31    }
32
33    /// The first argument (the command name), or `None` if empty.
34    fn first(&self) -> Option<&[u8]> {
35        self.get(0)
36    }
37
38    /// Iterate the arguments as byte slices.
39    fn iter(&self) -> ArgvIter<'_, Self>
40    where
41        Self: Sized,
42    {
43        ArgvIter { view: self, i: 0 }
44    }
45
46    /// Clear `out` and refill it with this view's arguments. `out` keeps
47    /// its buffer capacity across the clear, so refilling a recycled
48    /// [`Argv`] (see [`crate::ArgvPool`]) is allocation-free in steady
49    /// state. Object-safe (no `Self: Sized` bound).
50    fn copy_into(&self, out: &mut Argv) {
51        out.clear();
52        let n = self.len();
53        let total: usize = (0..n).map(|i| self.get(i).map_or(0, <[u8]>::len)).sum();
54        out.reserve_for(n, total);
55        for i in 0..n {
56            if let Some(arg) = self.get(i) {
57                out.push(arg);
58            }
59        }
60    }
61
62    /// Materialise an owned [`Argv`] — copies arg bytes into a fresh buffer.
63    /// Used at handoff junctures (cross-shard dispatch, MULTI queue, AOF
64    /// logging) that need to outlive the original input buffer. Object-safe
65    /// (no `Self: Sized` bound) so callers can hold `&dyn ArgvView`.
66    fn to_argv(&self) -> Argv {
67        let mut out = Argv::default();
68        self.copy_into(&mut out);
69        out
70    }
71}
72
73/// Iterator yielding each `ArgvView`'s arguments as `&[u8]` slices.
74///
75/// Returned by [`ArgvView::iter`]. Concrete (rather than `impl Iterator`) so
76/// the method works for both `Argv` and `ArgvBorrowed` callers.
77pub struct ArgvIter<'a, V: ?Sized + ArgvView> {
78    view: &'a V,
79    i: usize,
80}
81
82impl<'a, V: ?Sized + ArgvView> Iterator for ArgvIter<'a, V> {
83    type Item = &'a [u8];
84    fn next(&mut self) -> Option<&'a [u8]> {
85        let r = self.view.get(self.i);
86        if r.is_some() {
87            self.i += 1;
88        }
89        r
90    }
91    fn size_hint(&self) -> (usize, Option<usize>) {
92        let rem = self.view.len().saturating_sub(self.i);
93        (rem, Some(rem))
94    }
95}
96
97impl<V: ?Sized + ArgvView> ExactSizeIterator for ArgvIter<'_, V> {}
98
99impl ArgvView for Argv {
100    fn len(&self) -> usize {
101        Argv::len(self)
102    }
103    fn get(&self, i: usize) -> Option<&[u8]> {
104        Argv::get(self, i)
105    }
106}
107
108impl ArgvView for ArgvBorrowed<'_> {
109    fn len(&self) -> usize {
110        ArgvBorrowed::len(self)
111    }
112    fn get(&self, i: usize) -> Option<&[u8]> {
113        ArgvBorrowed::get(self, i)
114    }
115}
116
117#[cfg(test)]
118mod tests {
119    use super::*;
120
121    fn first_arg<A: ArgvView>(a: &A) -> Option<&[u8]> {
122        a.first()
123    }
124
125    fn arg_at<A: ArgvView>(a: &A, i: usize) -> &[u8] {
126        &a[i]
127    }
128
129    fn collect_iter<A: ArgvView>(a: &A) -> Vec<Vec<u8>> {
130        a.iter().map(|s| s.to_vec()).collect()
131    }
132
133    #[test]
134    fn argv_implements_argv_view() {
135        let mut a = Argv::default();
136        a.push(b"SET");
137        a.push(b"k");
138        a.push(b"v");
139        assert_eq!(ArgvView::len(&a), 3);
140        assert_eq!(first_arg(&a), Some(b"SET" as &[u8]));
141        assert_eq!(arg_at(&a, 1), b"k" as &[u8]);
142        assert_eq!(
143            collect_iter(&a),
144            vec![b"SET".to_vec(), b"k".to_vec(), b"v".to_vec()]
145        );
146    }
147
148    #[test]
149    fn argv_borrowed_implements_argv_view() {
150        let buf: &[u8] = b"abcdef";
151        let mut a = ArgvBorrowed::new(buf);
152        a.push_range(0, 3); // abc
153        a.push_range(3, 6); // def
154        assert_eq!(ArgvView::len(&a), 2);
155        assert_eq!(first_arg(&a), Some(b"abc" as &[u8]));
156        assert_eq!(arg_at(&a, 1), b"def" as &[u8]);
157        assert_eq!(collect_iter(&a), vec![b"abc".to_vec(), b"def".to_vec()]);
158    }
159
160    #[test]
161    fn iter_size_hint_is_exact() {
162        let mut a = Argv::default();
163        a.push(b"A");
164        a.push(b"B");
165        a.push(b"C");
166        let it = ArgvView::iter(&a);
167        assert_eq!(it.size_hint(), (3, Some(3)));
168        assert_eq!(it.len(), 3);
169    }
170
171    #[test]
172    fn empty_argv_iter_yields_nothing() {
173        let a = Argv::default();
174        assert!(ArgvView::is_empty(&a));
175        assert_eq!(first_arg(&a), None);
176        let mut it = ArgvView::iter(&a);
177        assert!(it.next().is_none());
178    }
179
180    #[test]
181    fn generic_over_owned_and_borrowed_with_same_api() {
182        // The point of ArgvView: verb code reads the same regardless of owner.
183        fn route_name<A: ArgvView>(a: &A) -> &[u8] {
184            a.first().unwrap_or(b"")
185        }
186        let mut owned = Argv::default();
187        owned.push(b"PING");
188        let buf: &[u8] = b"PING";
189        let mut borrowed = ArgvBorrowed::new(buf);
190        borrowed.push_range(0, 4);
191        assert_eq!(route_name(&owned), b"PING");
192        assert_eq!(route_name(&borrowed), b"PING");
193    }
194}