use super::modifiers::{format_bulk_string, Arity};
#[cfg(feature = "acl")]
#[derive(Debug)]
pub struct SetUser<S> {
pub name: S,
pub password: Option<S>,
pub commands: Option<Vec<S>>,
pub keys: Option<S>,
}
#[cfg(feature = "acl")]
#[derive(Debug)]
pub enum AclCommand<S> {
List,
SetUser(SetUser<S>),
DelUser(Arity<S>),
}
#[cfg(feature = "acl")]
impl<S> std::fmt::Display for AclCommand<S>
where
S: std::fmt::Display,
{
fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
AclCommand::List => write!(formatter, "*2\r\n$3\r\nACL\r\n$4\r\nLIST\r\n"),
AclCommand::DelUser(Arity::One(inner)) => {
write!(
formatter,
"*3\r\n$3\r\nACL\r\n{}{}",
format_bulk_string("DELUSER"),
format_bulk_string(inner)
)
}
AclCommand::DelUser(Arity::Many(inner)) => {
let amount = 2 + inner.len();
write!(
formatter,
"*{}\r\n$3\r\nACL\r\n{}{}",
amount,
format_bulk_string("DELUSER"),
inner.iter().map(format_bulk_string).collect::<String>(),
)
}
AclCommand::SetUser(inner) => match (&inner.password, &inner.commands, &inner.keys) {
(Some(password), Some(command_pattern), Some(key_pattern)) => {
let count = 6 + command_pattern.len();
write!(
formatter,
"*{count}\r\n$3\r\nACL\r\n{}{}{}{}{}{}",
format_bulk_string("SETUSER"),
format_bulk_string(&inner.name),
format_bulk_string("on"),
format_bulk_string(format!(">{password}")),
format_bulk_string(format!("~{key_pattern}")),
command_pattern.iter().fold(String::new(), |acc, command| acc
+ format_bulk_string(format!("+{command}")).as_str())
)
}
(_, _, _) => Ok(()),
},
}
}
}
#[cfg(test)]
mod tests {
use super::{AclCommand, SetUser};
use crate::modifiers::{humanize_command, Arity};
#[test]
fn format_list() {
let command: AclCommand<&str> = AclCommand::List;
assert_eq!(format!("{command}"), "*2\r\n$3\r\nACL\r\n$4\r\nLIST\r\n");
}
#[test]
fn format_full_setuser() {
let command = AclCommand::SetUser(SetUser {
name: "library-member",
password: Some("many-books"),
commands: Some(vec!["hgetall"]),
keys: Some("books"),
});
assert_eq!(format!("{}", command), "*7\r\n$3\r\nACL\r\n$7\r\nSETUSER\r\n$14\r\nlibrary-member\r\n$2\r\non\r\n$11\r\n>many-books\r\n$6\r\n~books\r\n$8\r\n+hgetall\r\n");
assert_eq!(
humanize_command::<&str, &str>(&crate::Command::Acl(command)),
"ACL SETUSER library-member on >many-books ~books +hgetall"
);
}
#[test]
fn format_full_setuser_multi_command() {
let command = AclCommand::SetUser(SetUser {
name: "library-member",
password: Some("many-books"),
commands: Some(vec!["hgetall", "blpop"]),
keys: Some("books"),
});
assert_eq!(format!("{}", command), "*8\r\n$3\r\nACL\r\n$7\r\nSETUSER\r\n$14\r\nlibrary-member\r\n$2\r\non\r\n$11\r\n>many-books\r\n$6\r\n~books\r\n$8\r\n+hgetall\r\n$6\r\n+blpop\r\n");
assert_eq!(
humanize_command::<&str, &str>(&crate::Command::Acl(command)),
"ACL SETUSER library-member on >many-books ~books +hgetall +blpop"
);
}
#[test]
fn format_deluser_one() {
let command = AclCommand::DelUser(Arity::One("my-user"));
assert_eq!(
format!("{}", command),
"*3\r\n$3\r\nACL\r\n$7\r\nDELUSER\r\n$7\r\nmy-user\r\n"
);
assert_eq!(
humanize_command::<&str, &str>(&crate::Command::Acl(command)),
"ACL DELUSER my-user"
);
}
#[test]
fn format_deluser_many() {
let command = AclCommand::DelUser(Arity::Many(vec!["my-user", "other-user"]));
assert_eq!(
format!("{}", command),
"*4\r\n$3\r\nACL\r\n$7\r\nDELUSER\r\n$7\r\nmy-user\r\n$10\r\nother-user\r\n"
);
assert_eq!(
humanize_command::<&str, &str>(&crate::Command::Acl(command)),
"ACL DELUSER my-user other-user"
);
}
#[test]
fn format_partial_setuser() {
let command = AclCommand::SetUser(SetUser {
name: "library-member",
password: None,
commands: None,
keys: None,
});
assert_eq!(format!("{}", command), "")
}
}