1#[cfg(feature = "aio")]
2use futures_util::{
3 Stream, StreamExt,
4 future::BoxFuture,
5 task::{Context, Poll},
6};
7#[cfg(feature = "aio")]
8use std::pin::Pin;
9#[cfg(feature = "cache-aio")]
10use std::time::Duration;
11use std::{fmt, io, io::Write};
12
13use crate::pipeline::Pipeline;
14use crate::types::{FromRedisValue, RedisResult, RedisWrite, ToRedisArgs, from_redis_value};
15use crate::{ParsingError, connection::ConnectionLike};
16
17#[derive(Clone, PartialEq, Debug)]
19#[non_exhaustive]
20pub enum Arg<D> {
21 Simple(D),
23 Cursor,
25}
26
27#[cfg(feature = "cache-aio")]
40#[cfg_attr(docsrs, doc(cfg(feature = "cache-aio")))]
41#[derive(Clone, Debug)]
42pub struct CommandCacheConfig {
43 pub(crate) enable_cache: bool,
44 pub(crate) client_side_ttl: Option<Duration>,
45}
46
47#[cfg(feature = "cache-aio")]
48impl CommandCacheConfig {
49 pub fn new() -> Self {
51 Self {
52 enable_cache: true,
53 client_side_ttl: None,
54 }
55 }
56
57 pub fn set_enable_cache(mut self, enable_cache: bool) -> Self {
60 self.enable_cache = enable_cache;
61 self
62 }
63
64 pub fn set_client_side_ttl(mut self, client_side_ttl: Duration) -> Self {
66 self.client_side_ttl = Some(client_side_ttl);
67 self
68 }
69}
70#[cfg(feature = "cache-aio")]
71impl Default for CommandCacheConfig {
72 fn default() -> Self {
73 Self::new()
74 }
75}
76
77#[derive(Clone)]
79pub struct Cmd {
80 pub(crate) data: Vec<u8>,
81 args: Vec<Arg<usize>>,
83 cursor: Option<u64>,
84 no_response: bool,
86 pub(crate) skip_concurrency_limit: bool,
87 #[cfg(feature = "cache-aio")]
88 cache: Option<CommandCacheConfig>,
89}
90
91impl std::fmt::Debug for Cmd {
92 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93 let mut debug_struct = f.debug_struct("Cmd");
94 debug_struct
95 .field("data", &String::from_utf8_lossy(&self.data).as_ref())
96 .field("args", &self.args)
97 .field("cursor", &self.cursor)
98 .field("no_response", &self.no_response);
99
100 #[cfg(feature = "cache-aio")]
101 debug_struct.field("cache", &self.cache);
102
103 debug_struct.finish()
104 }
105}
106
107pub struct Iter<'a, T: FromRedisValue> {
109 iter: CheckedIter<'a, T>,
110}
111impl<T: FromRedisValue> Iterator for Iter<'_, T> {
112 type Item = RedisResult<T>;
113
114 #[inline]
115 fn next(&mut self) -> Option<RedisResult<T>> {
116 self.iter.next()
117 }
118}
119
120struct CheckedIter<'a, T: FromRedisValue> {
122 batch: std::vec::IntoIter<Result<T, ParsingError>>,
123 con: &'a mut (dyn ConnectionLike + 'a),
124 cmd: Cmd,
125}
126
127impl<T: FromRedisValue> Iterator for CheckedIter<'_, T> {
128 type Item = RedisResult<T>;
129
130 #[inline]
131 fn next(&mut self) -> Option<RedisResult<T>> {
132 loop {
137 if let Some(value) = self.batch.next() {
138 return Some(value.map_err(|err| err.into()));
139 };
140
141 if self.cmd.cursor? == 0 {
142 return None;
143 }
144
145 let (cursor, batch) = match self
146 .con
147 .req_packed_command(&self.cmd.get_packed_command())
148 .and_then(|val| Ok(from_redis_value::<(u64, _)>(val)?))
149 {
150 Ok((cursor, values)) => (cursor, T::from_each_redis_values(values)),
151 Err(e) => return Some(Err(e)),
152 };
153
154 self.cmd.cursor = Some(cursor);
155 self.batch = batch.into_iter();
156 }
157 }
158}
159
160#[cfg(feature = "aio")]
161use crate::aio::ConnectionLike as AsyncConnection;
162
163#[cfg(feature = "aio")]
165struct AsyncIterInner<'a, T: FromRedisValue + 'a> {
166 batch: std::vec::IntoIter<Result<T, ParsingError>>,
167 con: &'a mut (dyn AsyncConnection + Send + 'a),
168 cmd: Cmd,
169}
170
171#[cfg(feature = "aio")]
173enum IterOrFuture<'a, T: FromRedisValue + 'a> {
174 Iter(AsyncIterInner<'a, T>),
175 Future(BoxFuture<'a, (AsyncIterInner<'a, T>, Option<RedisResult<T>>)>),
176 Empty,
177}
178
179#[cfg(feature = "aio")]
181pub struct AsyncIter<'a, T: FromRedisValue + 'a> {
182 inner: IterOrFuture<'a, T>,
183}
184
185#[cfg(feature = "aio")]
186impl<'a, T: FromRedisValue + 'a> AsyncIterInner<'a, T> {
187 async fn next_item(&mut self) -> Option<RedisResult<T>> {
188 loop {
193 if let Some(v) = self.batch.next() {
194 return Some(v.map_err(|err| err.into()));
195 };
196
197 if self.cmd.cursor? == 0 {
198 return None;
199 }
200
201 let (cursor, batch) = match self
202 .con
203 .req_packed_command(&self.cmd)
204 .await
205 .and_then(|val| Ok(from_redis_value::<(u64, _)>(val)?))
206 {
207 Ok((cursor, items)) => (cursor, T::from_each_redis_values(items)),
208 Err(e) => return Some(Err(e)),
209 };
210
211 self.cmd.cursor = Some(cursor);
212 self.batch = batch.into_iter();
213 }
214 }
215}
216
217#[cfg(feature = "aio")]
218impl<'a, T: FromRedisValue + 'a + Unpin + Send> AsyncIter<'a, T> {
219 #[inline]
235 pub async fn next_item(&mut self) -> Option<RedisResult<T>> {
236 StreamExt::next(self).await
237 }
238}
239
240#[cfg(feature = "aio")]
241impl<'a, T: FromRedisValue + Unpin + Send + 'a> Stream for AsyncIter<'a, T> {
242 type Item = RedisResult<T>;
243
244 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
245 let this = self.get_mut();
246 let inner = std::mem::replace(&mut this.inner, IterOrFuture::Empty);
247 match inner {
248 IterOrFuture::Iter(mut iter) => {
249 let fut = async move {
250 let next_item = iter.next_item().await;
251 (iter, next_item)
252 };
253 this.inner = IterOrFuture::Future(Box::pin(fut));
254 Pin::new(this).poll_next(cx)
255 }
256 IterOrFuture::Future(mut fut) => match fut.as_mut().poll(cx) {
257 Poll::Pending => {
258 this.inner = IterOrFuture::Future(fut);
259 Poll::Pending
260 }
261 Poll::Ready((iter, value)) => {
262 this.inner = IterOrFuture::Iter(iter);
263
264 Poll::Ready(value)
265 }
266 },
267 IterOrFuture::Empty => unreachable!(),
268 }
269 }
270}
271
272fn countdigits(mut v: usize) -> usize {
273 let mut result = 1;
274 loop {
275 if v < 10 {
276 return result;
277 }
278 if v < 100 {
279 return result + 1;
280 }
281 if v < 1000 {
282 return result + 2;
283 }
284 if v < 10000 {
285 return result + 3;
286 }
287
288 v /= 10000;
289 result += 4;
290 }
291}
292
293#[inline]
294fn bulklen(len: usize) -> usize {
295 1 + countdigits(len) + 2 + len + 2
296}
297
298fn args_len<'a, I>(args: I, cursor: u64) -> usize
299where
300 I: IntoIterator<Item = Arg<&'a [u8]>> + ExactSizeIterator,
301{
302 let mut totlen = 1 + countdigits(args.len()) + 2;
303 for item in args {
304 totlen += bulklen(match item {
305 Arg::Cursor => countdigits(cursor as usize),
306 Arg::Simple(val) => val.len(),
307 });
308 }
309 totlen
310}
311
312pub(crate) fn cmd_len(cmd: &Cmd) -> usize {
313 args_len(cmd.args_iter(), cmd.cursor.unwrap_or(0))
314}
315
316fn encode_command<'a, I>(args: I, cursor: u64) -> Vec<u8>
317where
318 I: IntoIterator<Item = Arg<&'a [u8]>> + Clone + ExactSizeIterator,
319{
320 let mut cmd = Vec::new();
321 write_command_to_vec(&mut cmd, args, cursor);
322 cmd
323}
324
325fn write_command_to_vec<'a, I>(cmd: &mut Vec<u8>, args: I, cursor: u64)
326where
327 I: IntoIterator<Item = Arg<&'a [u8]>> + Clone + ExactSizeIterator,
328{
329 let totlen = args_len(args.clone(), cursor);
330
331 cmd.reserve(totlen);
332
333 write_command(cmd, args, cursor).unwrap()
334}
335
336fn write_command<'a, I>(cmd: &mut (impl ?Sized + Write), args: I, cursor: u64) -> io::Result<()>
337where
338 I: IntoIterator<Item = Arg<&'a [u8]>> + Clone + ExactSizeIterator,
339{
340 let mut buf = ::itoa::Buffer::new();
341
342 cmd.write_all(b"*")?;
343 let s = buf.format(args.len());
344 cmd.write_all(s.as_bytes())?;
345 cmd.write_all(b"\r\n")?;
346
347 let mut cursor_bytes = itoa::Buffer::new();
348 for item in args {
349 let bytes = match item {
350 Arg::Cursor => cursor_bytes.format(cursor).as_bytes(),
351 Arg::Simple(val) => val,
352 };
353
354 cmd.write_all(b"$")?;
355 let s = buf.format(bytes.len());
356 cmd.write_all(s.as_bytes())?;
357 cmd.write_all(b"\r\n")?;
358
359 cmd.write_all(bytes)?;
360 cmd.write_all(b"\r\n")?;
361 }
362 Ok(())
363}
364
365impl RedisWrite for Cmd {
366 fn write_arg(&mut self, arg: &[u8]) {
367 self.data.extend_from_slice(arg);
368 self.args.push(Arg::Simple(self.data.len()));
369 }
370
371 fn write_arg_fmt(&mut self, arg: impl fmt::Display) {
372 write!(self.data, "{arg}").unwrap();
373 self.args.push(Arg::Simple(self.data.len()));
374 }
375
376 fn writer_for_next_arg(&mut self) -> impl Write + '_ {
377 struct CmdBufferedArgGuard<'a>(&'a mut Cmd);
378 impl Drop for CmdBufferedArgGuard<'_> {
379 fn drop(&mut self) {
380 self.0.args.push(Arg::Simple(self.0.data.len()));
381 }
382 }
383 impl Write for CmdBufferedArgGuard<'_> {
384 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
385 self.0.data.extend_from_slice(buf);
386 Ok(buf.len())
387 }
388
389 fn flush(&mut self) -> std::io::Result<()> {
390 Ok(())
391 }
392 }
393
394 CmdBufferedArgGuard(self)
395 }
396
397 fn reserve_space_for_args(&mut self, additional: impl IntoIterator<Item = usize>) {
398 let mut capacity = 0;
399 let mut args = 0;
400 for add in additional {
401 capacity += add;
402 args += 1;
403 }
404 self.data.reserve(capacity);
405 self.args.reserve(args);
406 }
407
408 #[cfg(feature = "bytes")]
409 fn bufmut_for_next_arg(&mut self, capacity: usize) -> impl bytes::BufMut + '_ {
410 self.data.reserve(capacity);
411 struct CmdBufferedArgGuard<'a>(&'a mut Cmd);
412 impl Drop for CmdBufferedArgGuard<'_> {
413 fn drop(&mut self) {
414 self.0.args.push(Arg::Simple(self.0.data.len()));
415 }
416 }
417 unsafe impl bytes::BufMut for CmdBufferedArgGuard<'_> {
418 fn remaining_mut(&self) -> usize {
419 self.0.data.remaining_mut()
420 }
421
422 unsafe fn advance_mut(&mut self, cnt: usize) {
423 unsafe {
424 self.0.data.advance_mut(cnt);
425 }
426 }
427
428 fn chunk_mut(&mut self) -> &mut bytes::buf::UninitSlice {
429 self.0.data.chunk_mut()
430 }
431
432 fn put<T: bytes::buf::Buf>(&mut self, src: T)
434 where
435 Self: Sized,
436 {
437 self.0.data.put(src);
438 }
439
440 fn put_slice(&mut self, src: &[u8]) {
441 self.0.data.put_slice(src);
442 }
443
444 fn put_bytes(&mut self, val: u8, cnt: usize) {
445 self.0.data.put_bytes(val, cnt);
446 }
447 }
448
449 CmdBufferedArgGuard(self)
450 }
451}
452
453impl Default for Cmd {
454 fn default() -> Cmd {
455 Cmd::new()
456 }
457}
458
459impl Cmd {
487 pub fn new() -> Cmd {
489 Cmd {
490 data: vec![],
491 args: vec![],
492 cursor: None,
493 no_response: false,
494 skip_concurrency_limit: false,
495 #[cfg(feature = "cache-aio")]
496 cache: None,
497 }
498 }
499
500 pub fn with_capacity(arg_count: usize, size_of_data: usize) -> Cmd {
502 Cmd {
503 data: Vec::with_capacity(size_of_data),
504 args: Vec::with_capacity(arg_count),
505 cursor: None,
506 no_response: false,
507 skip_concurrency_limit: false,
508 #[cfg(feature = "cache-aio")]
509 cache: None,
510 }
511 }
512
513 #[cfg(test)]
515 #[allow(dead_code)]
516 pub(crate) fn capacity(&self) -> (usize, usize) {
517 (self.args.capacity(), self.data.capacity())
518 }
519
520 pub fn clear(&mut self) {
539 self.data.clear();
540 self.args.clear();
541 self.cursor = None;
542 self.no_response = false;
543 self.skip_concurrency_limit = false;
544 #[cfg(feature = "cache-aio")]
545 {
546 self.cache = None;
547 }
548 }
549
550 #[inline]
564 pub fn arg<T: ToRedisArgs>(&mut self, arg: T) -> &mut Cmd {
565 arg.write_redis_args(self);
566 self
567 }
568
569 pub fn take(&mut self) -> Self {
573 std::mem::take(self)
574 }
575
576 #[inline]
594 pub fn cursor_arg(&mut self, cursor: u64) -> &mut Cmd {
595 self.cursor = Some(cursor);
596 self.args.push(Arg::Cursor);
597 self
598 }
599
600 #[inline]
606 pub fn get_packed_command(&self) -> Vec<u8> {
607 let mut cmd = Vec::new();
608 if self.is_empty() {
609 return cmd;
610 }
611 self.write_packed_command(&mut cmd);
612 cmd
613 }
614
615 #[inline]
623 pub fn write_packed_command(&self, dst: &mut Vec<u8>) {
624 write_command_to_vec(dst, self.args_iter(), self.cursor.unwrap_or(0))
625 }
626
627 pub(crate) fn write_packed_command_preallocated(&self, cmd: &mut Vec<u8>) {
628 write_command(cmd, self.args_iter(), self.cursor.unwrap_or(0)).unwrap()
629 }
630
631 #[inline]
633 pub fn in_scan_mode(&self) -> bool {
634 self.cursor.is_some()
635 }
636
637 #[inline]
641 pub fn query<T: FromRedisValue>(&self, con: &mut dyn ConnectionLike) -> RedisResult<T> {
642 match con.req_command(self) {
643 Ok(val) => Ok(from_redis_value(val.extract_error()?)?),
644 Err(e) => Err(e),
645 }
646 }
647
648 #[inline]
650 #[cfg(feature = "aio")]
651 pub async fn query_async<T: FromRedisValue>(
652 &self,
653 con: &mut impl crate::aio::ConnectionLike,
654 ) -> RedisResult<T> {
655 let val = con.req_packed_command(self).await?;
656 Ok(from_redis_value(val.extract_error()?)?)
657 }
658
659 fn set_cursor_and_get_batch<T: FromRedisValue>(
662 &mut self,
663 value: crate::Value,
664 ) -> RedisResult<Vec<Result<T, ParsingError>>> {
665 let (cursor, values) = if value.looks_like_cursor() {
666 let (cursor, values) = from_redis_value::<(u64, _)>(value)?;
667 (cursor, values)
668 } else {
669 (0, from_redis_value(value)?)
670 };
671
672 self.cursor = Some(cursor);
673
674 Ok(T::from_each_redis_values(values))
675 }
676
677 #[inline]
692 pub fn iter<T: FromRedisValue>(
693 mut self,
694 con: &mut dyn ConnectionLike,
695 ) -> RedisResult<Iter<'_, T>> {
696 let rv = con.req_command(&self)?;
697
698 let batch = self.set_cursor_and_get_batch(rv)?;
699
700 Ok(Iter {
701 iter: CheckedIter {
702 batch: batch.into_iter(),
703 con,
704 cmd: self,
705 },
706 })
707 }
708
709 #[cfg(feature = "aio")]
725 #[inline]
726 pub async fn iter_async<'a, T: FromRedisValue + 'a>(
727 mut self,
728 con: &'a mut (dyn AsyncConnection + Send),
729 ) -> RedisResult<AsyncIter<'a, T>> {
730 let rv = con.req_packed_command(&self).await?;
731
732 let batch = self.set_cursor_and_get_batch(rv)?;
733
734 Ok(AsyncIter {
735 inner: IterOrFuture::Iter(AsyncIterInner {
736 batch: batch.into_iter(),
737 con,
738 cmd: self,
739 }),
740 })
741 }
742
743 #[inline]
748 pub fn exec(&self, con: &mut dyn ConnectionLike) -> RedisResult<()> {
749 self.query::<()>(con)
750 }
751
752 #[cfg(feature = "aio")]
757 pub async fn exec_async(&self, con: &mut impl crate::aio::ConnectionLike) -> RedisResult<()> {
758 self.query_async::<()>(con).await
759 }
760
761 pub fn args_iter(&self) -> impl Clone + ExactSizeIterator<Item = Arg<&[u8]>> {
763 let mut prev = 0;
764 self.args.iter().map(move |arg| match *arg {
765 Arg::Simple(i) => {
766 let arg = Arg::Simple(&self.data[prev..i]);
767 prev = i;
768 arg
769 }
770
771 Arg::Cursor => Arg::Cursor,
772 })
773 }
774
775 #[cfg(any(feature = "cluster", feature = "cache-aio"))]
777 pub(crate) fn arg_idx(&self, idx: usize) -> Option<&[u8]> {
778 if idx >= self.args.len() {
779 return None;
780 }
781
782 let start = if idx == 0 {
783 0
784 } else {
785 match self.args[idx - 1] {
786 Arg::Simple(n) => n,
787 _ => 0,
788 }
789 };
790 let end = match self.args[idx] {
791 Arg::Simple(n) => n,
792 _ => 0,
793 };
794 if start == 0 && end == 0 {
795 return None;
796 }
797 Some(&self.data[start..end])
798 }
799
800 #[inline]
805 pub fn set_no_response(&mut self, nr: bool) -> &mut Cmd {
806 self.no_response = nr;
807 self
808 }
809
810 #[inline]
812 pub fn is_no_response(&self) -> bool {
813 self.no_response
814 }
815
816 #[cfg(feature = "cache-aio")]
818 #[cfg_attr(docsrs, doc(cfg(feature = "cache-aio")))]
819 pub fn set_cache_config(&mut self, command_cache_config: CommandCacheConfig) -> &mut Cmd {
820 self.cache = Some(command_cache_config);
821 self
822 }
823
824 #[cfg(feature = "cache-aio")]
825 #[inline]
826 pub(crate) fn get_cache_config(&self) -> &Option<CommandCacheConfig> {
827 &self.cache
828 }
829
830 pub(crate) fn is_empty(&self) -> bool {
831 self.args.is_empty()
832 }
833}
834
835pub fn cmd(name: &str) -> Cmd {
845 let mut rv = Cmd::new();
846 rv.arg(name);
847 rv
848}
849
850pub fn pack_command(args: &[Vec<u8>]) -> Vec<u8> {
869 encode_command(args.iter().map(|x| Arg::Simple(&x[..])), 0)
870}
871
872pub fn pipe() -> Pipeline {
874 Pipeline::new()
875}
876
877#[cfg(test)]
878mod tests {
879 use super::*;
880 #[cfg(feature = "bytes")]
881 use bytes::BufMut;
882
883 fn args_iter_to_str(cmd: &Cmd) -> Vec<String> {
884 cmd.args_iter()
885 .map(|arg| match arg {
886 Arg::Simple(bytes) => String::from_utf8(bytes.to_vec()).unwrap(),
887 Arg::Cursor => "CURSOR".to_string(),
888 })
889 .collect()
890 }
891
892 fn assert_arg_equality(c1: &Cmd, c2: &Cmd) {
893 let v1: Vec<_> = c1.args_iter().collect::<Vec<_>>();
894 let v2: Vec<_> = c2.args_iter().collect::<Vec<_>>();
895 assert_eq!(
896 v1,
897 v2,
898 "{:?} - {:?}",
899 args_iter_to_str(c1),
900 args_iter_to_str(c2)
901 );
902 }
903
904 fn assert_practical_equivalent(c1: Cmd, c2: Cmd) {
905 assert_eq!(c1.get_packed_command(), c2.get_packed_command());
906 assert_arg_equality(&c1, &c2);
907 }
908
909 #[test]
910 fn test_cmd_packed_command_simple_args() {
911 let args: &[&[u8]] = &[b"phone", b"barz"];
912 let mut cmd = cmd("key");
913 cmd.write_arg_fmt("value");
914 cmd.arg(42).arg(args);
915
916 let packed_command = cmd.get_packed_command();
917 assert_eq!(cmd_len(&cmd), packed_command.len());
918 assert_eq!(
919 packed_command,
920 b"*5\r\n$3\r\nkey\r\n$5\r\nvalue\r\n$2\r\n42\r\n$5\r\nphone\r\n$4\r\nbarz\r\n",
921 "{}",
922 String::from_utf8(packed_command.clone()).unwrap()
923 );
924 let args_vec: Vec<&[u8]> = vec![b"key", b"value", b"42", b"phone", b"barz"];
925 let args_vec: Vec<_> = args_vec.into_iter().map(Arg::Simple).collect();
926 assert_eq!(cmd.args_iter().collect::<Vec<_>>(), args_vec);
927 }
928
929 #[test]
930 fn test_cmd_packed_command_with_cursor() {
931 let args: &[&[u8]] = &[b"phone", b"barz"];
932 let mut cmd = cmd("key");
933 cmd.arg("value").arg(42).arg(args).cursor_arg(512);
934
935 let packed_command = cmd.get_packed_command();
936 assert_eq!(cmd_len(&cmd), packed_command.len());
937 assert_eq!(
938 packed_command,
939 b"*6\r\n$3\r\nkey\r\n$5\r\nvalue\r\n$2\r\n42\r\n$5\r\nphone\r\n$4\r\nbarz\r\n$3\r\n512\r\n",
940 "{}",
941 String::from_utf8(packed_command.clone()).unwrap()
942 );
943 let args_vec: Vec<&[u8]> = vec![b"key", b"value", b"42", b"phone", b"barz"];
944 let args_vec: Vec<_> = args_vec
945 .into_iter()
946 .map(Arg::Simple)
947 .chain(std::iter::once(Arg::Cursor))
948 .collect();
949 assert_eq!(cmd.args_iter().collect::<Vec<_>>(), args_vec);
950 }
951
952 #[test]
953 fn test_cmd_clean() {
954 let mut cmd = cmd("key");
955 cmd.arg("value")
956 .cursor_arg(24)
957 .set_no_response(true)
958 .clear();
959
960 assert!(cmd.data.is_empty());
962 assert!(cmd.data.capacity() > 0);
963 assert!(cmd.is_empty());
964 assert!(cmd.args.capacity() > 0);
965 assert_eq!(cmd.cursor, None);
966 assert!(!cmd.no_response);
967 assert_practical_equivalent(cmd, Cmd::new());
968 }
969
970 #[test]
971 #[cfg(feature = "cache-aio")]
972 fn test_cmd_clean_cache_aio() {
973 let mut cmd = cmd("key");
974 cmd.arg("value")
975 .cursor_arg(24)
976 .set_cache_config(crate::CommandCacheConfig::default())
977 .set_no_response(true)
978 .clear();
979
980 assert!(cmd.data.is_empty());
982 assert!(cmd.data.capacity() > 0);
983 assert!(cmd.is_empty());
984 assert!(cmd.args.capacity() > 0);
985 assert_eq!(cmd.cursor, None);
986 assert!(!cmd.no_response);
987 assert!(cmd.cache.is_none());
988 }
989
990 #[test]
991 fn test_cmd_writer_for_next_arg() {
992 let mut c1 = Cmd::new();
995 {
996 let mut c1_writer = c1.writer_for_next_arg();
997 c1_writer.write_all(b"foo").unwrap();
998 c1_writer.write_all(b"bar").unwrap();
999 c1_writer.flush().unwrap();
1000 }
1001
1002 let mut c2 = Cmd::new();
1003 c2.write_arg(b"foobar");
1004
1005 assert_practical_equivalent(c1, c2);
1006 }
1007
1008 #[test]
1011 fn test_cmd_writer_for_next_arg_multiple() {
1012 let mut c1 = Cmd::new();
1013 {
1014 let mut c1_writer = c1.writer_for_next_arg();
1015 c1_writer.write_all(b"foo").unwrap();
1016 c1_writer.write_all(b"bar").unwrap();
1017 c1_writer.flush().unwrap();
1018 }
1019 {
1020 let mut c1_writer = c1.writer_for_next_arg();
1021 c1_writer.write_all(b"baz").unwrap();
1022 c1_writer.write_all(b"qux").unwrap();
1023 c1_writer.flush().unwrap();
1024 }
1025
1026 let mut c2 = Cmd::new();
1027 c2.write_arg(b"foobar");
1028 c2.write_arg(b"bazqux");
1029
1030 assert_practical_equivalent(c1, c2);
1031 }
1032
1033 #[test]
1035 fn test_cmd_writer_for_next_arg_empty() {
1036 let mut c1 = Cmd::new();
1037 {
1038 let mut c1_writer = c1.writer_for_next_arg();
1039 c1_writer.flush().unwrap();
1040 }
1041
1042 let mut c2 = Cmd::new();
1043 c2.write_arg(b"");
1044
1045 assert_practical_equivalent(c1, c2);
1046 }
1047
1048 #[cfg(feature = "bytes")]
1049 #[test]
1052 fn test_cmd_bufmut_for_next_arg() {
1053 let mut c1 = Cmd::new();
1054 {
1055 let mut c1_writer = c1.bufmut_for_next_arg(6);
1056 c1_writer.put_slice(b"foo");
1057 c1_writer.put_slice(b"bar");
1058 }
1059
1060 let mut c2 = Cmd::new();
1061 c2.write_arg(b"foobar");
1062
1063 assert_practical_equivalent(c1, c2);
1064 }
1065
1066 #[cfg(feature = "bytes")]
1067 #[test]
1070 fn test_cmd_bufmut_for_next_arg_multiple() {
1071 let mut c1 = Cmd::new();
1072 {
1073 let mut c1_writer = c1.bufmut_for_next_arg(6);
1074 c1_writer.put_slice(b"foo");
1075 c1_writer.put_slice(b"bar");
1076 }
1077 {
1078 let mut c1_writer = c1.bufmut_for_next_arg(6);
1079 c1_writer.put_slice(b"baz");
1080 c1_writer.put_slice(b"qux");
1081 }
1082
1083 let mut c2 = Cmd::new();
1084 c2.write_arg(b"foobar");
1085 c2.write_arg(b"bazqux");
1086
1087 assert_practical_equivalent(c1, c2);
1088 }
1089
1090 #[cfg(feature = "bytes")]
1091 #[test]
1093 fn test_cmd_bufmut_for_next_arg_empty() {
1094 let mut c1 = Cmd::new();
1095 {
1096 let _c1_writer = c1.bufmut_for_next_arg(0);
1097 }
1098
1099 let mut c2 = Cmd::new();
1100 c2.write_arg(b"");
1101
1102 assert_practical_equivalent(c1, c2);
1103 }
1104
1105 #[test]
1106 #[cfg(feature = "cluster")]
1107 fn test_cmd_arg_idx() {
1108 let mut c = Cmd::new();
1109 assert_eq!(c.arg_idx(0), None);
1110
1111 c.arg("SET");
1112 assert_eq!(c.arg_idx(0), Some(&b"SET"[..]));
1113 assert_eq!(c.arg_idx(1), None);
1114
1115 c.arg("foo").arg("42");
1116 assert_eq!(c.arg_idx(1), Some(&b"foo"[..]));
1117 assert_eq!(c.arg_idx(2), Some(&b"42"[..]));
1118 assert_eq!(c.arg_idx(3), None);
1119 assert_eq!(c.arg_idx(4), None);
1120 }
1121}