Skip to main content

kevy_cli/
lib.rs

1//! CLI-shaped Reply formatter for the kevy-cli REPL.
2//!
3//! Only `format_reply` lives here — the protocol pieces (TCP connect, request
4//! loop) live in the [`kevy-resp-client`](https://crates.io/crates/kevy-resp-client)
5//! crate so they're reusable by integration tests / scripts / other tools.
6//! This file is the CLI-specific bit (how a redis-cli user expects bulk
7//! strings quoted, arrays numbered, nil shown as `(nil)`).
8
9#![forbid(unsafe_code)]
10#![warn(missing_docs)]
11
12pub use kevy_resp::Reply;
13
14/// Backup / restore container support. See
15/// [`backup::pack`] and [`backup::unpack`].
16pub mod backup;
17
18/// Migration toolchain (`export` / `import`). See
19/// [`migrate::run_export`] and [`migrate::run_import`].
20pub mod migrate;
21
22/// Prefix bulk ops + diagnostics (`copy-prefix` /
23/// `delete-prefix` / `digest` / `diff` / `inspect`).
24pub mod bulk;
25
26/// Pretty-print a reply roughly the way `redis-cli` does. Arrays are
27/// numbered + indented; bulk strings are quoted; nil shows as `(nil)`.
28pub fn format_reply(reply: &Reply, indent: usize) -> String {
29    match reply {
30        Reply::Simple(s) => String::from_utf8_lossy(s).into_owned(),
31        Reply::Error(s) | Reply::BlobError(s) => {
32            format!("(error) {}", String::from_utf8_lossy(s))
33        }
34        Reply::Int(n) => format!("(integer) {n}"),
35        Reply::Bulk(b) => format!("\"{}\"", String::from_utf8_lossy(b)),
36        Reply::Nil | Reply::Null => "(nil)".to_string(),
37        Reply::Array(items) if items.is_empty() => "(empty array)".to_string(),
38        Reply::Array(items) | Reply::Set(items) | Reply::Push(items) => {
39            let pad = "   ".repeat(indent);
40            items
41                .iter()
42                .enumerate()
43                .map(|(i, it)| format!("{pad}{}) {}", i + 1, format_reply(it, indent + 1)))
44                .collect::<Vec<_>>()
45                .join("\n")
46        }
47        // RESP3 additions: format the same way redis-cli does today.
48        Reply::Map(pairs) if pairs.is_empty() => "(empty map)".to_string(),
49        Reply::Map(pairs) => {
50            let pad = "   ".repeat(indent);
51            pairs
52                .iter()
53                .enumerate()
54                .map(|(i, (k, v))| {
55                    format!(
56                        "{pad}{}) {} => {}",
57                        i + 1,
58                        format_reply(k, indent + 1),
59                        format_reply(v, indent + 1)
60                    )
61                })
62                .collect::<Vec<_>>()
63                .join("\n")
64        }
65        Reply::Double(v) => format!("(double) {v}"),
66        Reply::Boolean(b) => format!("(boolean) {}", if *b { "t" } else { "f" }),
67        Reply::Verbatim { fmt, data } => format!(
68            "(verbatim/{}) \"{}\"",
69            String::from_utf8_lossy(fmt),
70            String::from_utf8_lossy(data)
71        ),
72        Reply::BigNumber(s) => format!("(bignum) {}", String::from_utf8_lossy(s)),
73    }
74}