Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ nix = { version = "0.26", default-features = false }
cfg-if = "1"
redis-module-macros-internals = { path = "./redismodule-rs-macros-internals" }
log = "0.4"
common = { path = "./common" }

[dev-dependencies]
anyhow = "1"
Expand Down
7 changes: 7 additions & 0 deletions common/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "common"
version = "0.1.0"
edition = "2024"

[dependencies]
serde = { version = "1.0", features = ["derive"] }
109 changes: 109 additions & 0 deletions common/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
use serde::Deserialize;

#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Default)]
pub enum AclCategory {
#[default]
None,
Keyspace,
Read,
Write,
Set,
SortedSet,
List,
Hash,
String,
Bitmap,
HyperLogLog,
Geo,
Stream,
PubSub,
Admin,
Fast,
Slow,
Blocking,
Dangerous,
Connection,
Transaction,
Scripting,
Single(String),
Multi(Vec<AclCategory>),
}

impl From<Vec<AclCategory>> for AclCategory {
fn from(value: Vec<AclCategory>) -> Self {
AclCategory::Multi(value)
}
}

impl From<&str> for AclCategory {
fn from(value: &str) -> Self {
match value {
"" => AclCategory::None,
"keyspace" => AclCategory::Keyspace,
"read" => AclCategory::Read,
"write" => AclCategory::Write,
"set" => AclCategory::Set,
"sortedset" => AclCategory::SortedSet,
"list" => AclCategory::List,
"hash" => AclCategory::Hash,
"string" => AclCategory::String,
"bitmap" => AclCategory::Bitmap,
"hyperloglog" => AclCategory::HyperLogLog,
"geo" => AclCategory::Geo,
"stream" => AclCategory::Stream,
"pubsub" => AclCategory::PubSub,
"admin" => AclCategory::Admin,
"fast" => AclCategory::Fast,
"slow" => AclCategory::Slow,
"blocking" => AclCategory::Blocking,
"dangerous" => AclCategory::Dangerous,
"connection" => AclCategory::Connection,
"transaction" => AclCategory::Transaction,
"scripting" => AclCategory::Scripting,
_ if !value.contains(" ") => AclCategory::Single(value.to_string()),
_ => AclCategory::Multi(value.split_whitespace().map(AclCategory::from).collect()),
}
}
}

impl From<AclCategory> for String {
fn from(value: AclCategory) -> Self {
match value {
AclCategory::None => "".to_string(),
AclCategory::Keyspace => "keyspace".to_string(),
AclCategory::Read => "read".to_string(),
AclCategory::Write => "write".to_string(),
AclCategory::Set => "set".to_string(),
AclCategory::SortedSet => "sortedset".to_string(),
AclCategory::List => "list".to_string(),
AclCategory::Hash => "hash".to_string(),
AclCategory::String => "string".to_string(),
AclCategory::Bitmap => "bitmap".to_string(),
AclCategory::HyperLogLog => "hyperloglog".to_string(),
AclCategory::Geo => "geo".to_string(),
AclCategory::Stream => "stream".to_string(),
AclCategory::PubSub => "pubsub".to_string(),
AclCategory::Admin => "admin".to_string(),
AclCategory::Fast => "fast".to_string(),
AclCategory::Slow => "slow".to_string(),
AclCategory::Blocking => "blocking".to_string(),
AclCategory::Dangerous => "dangerous".to_string(),
AclCategory::Connection => "connection".to_string(),
AclCategory::Transaction => "transaction".to_string(),
AclCategory::Scripting => "scripting".to_string(),
AclCategory::Single(s) => s,
AclCategory::Multi(v) => v
.into_iter()
.map(String::from)
.collect::<Vec<_>>()
.join(" "),
}
}
}

impl std::fmt::Display for AclCategory {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", String::from(self.clone()))
}
}

1 change: 1 addition & 0 deletions redismodule-rs-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ quote = "1.0"
proc-macro2 = "1"
serde = { version = "1", features = ["derive"] }
serde_syn = "0.1.0"
common = { path = "../common" }

[lib]
name = "redis_module_macros"
Expand Down
132 changes: 132 additions & 0 deletions redismodule-rs-macros/src/command.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use common::AclCategory;
use proc_macro::TokenStream;
use proc_macro2::Ident;
use quote::quote;
Expand Down Expand Up @@ -223,6 +224,68 @@ pub struct KeySpecArg {
find_keys: FindKeys,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
pub enum CommandArgType {
String,
Integer,
Double,
Key,
Pattern,
UnixTime,
PureToken,
OneOf,
Block,
}

impl From<CommandArgType> for u32 {
fn from(arg_type: CommandArgType) -> Self {
match arg_type {
CommandArgType::String => 0,
CommandArgType::Integer => 1,
CommandArgType::Double => 2,
CommandArgType::Key => 3,
CommandArgType::Pattern => 4,
CommandArgType::UnixTime => 5,
CommandArgType::PureToken => 6,
CommandArgType::OneOf => 7,
CommandArgType::Block => 8,
}
}
}

#[derive(Debug, Clone, Deserialize)]
pub enum CommandArgFlags {
None,
Optional,
Multiple,
MultipleToken,
}

impl From<&CommandArgFlags> for &'static str {
fn from(value: &CommandArgFlags) -> Self {
match value {
CommandArgFlags::None => "NONE",
CommandArgFlags::Optional => "OPTIONAL",
CommandArgFlags::Multiple => "MULTIPLE",
CommandArgFlags::MultipleToken => "MULTIPLE_TOKEN",
}
}
}

#[derive(Debug, Clone, Deserialize)]
pub struct CommandArg {
pub name: String,
pub arg_type: CommandArgType,
pub key_spec_index: Option<u32>,
pub token: Option<String>,
pub summary: Option<String>,
pub since: Option<String>,
pub flags: Option<Vec<CommandArgFlags>>,
pub deprecated_since: Option<String>,
pub subargs: Option<Vec<CommandArg>>,
pub display_text: Option<String>,
}

#[derive(Debug, Deserialize)]
struct Args {
name: Option<String>,
Expand All @@ -234,6 +297,8 @@ struct Args {
tips: Option<String>,
arity: i64,
key_spec: Vec<KeySpecArg>,
args: Option<Vec<CommandArg>>,
acl_categories: Option<Vec<AclCategory>>,
}

impl Parse for Args {
Expand All @@ -247,6 +312,52 @@ fn to_token_stream(s: Option<String>) -> proc_macro2::TokenStream {
.unwrap_or(quote! {None})
}

fn generate_command_arg(arg: &CommandArg) -> proc_macro2::TokenStream {
let name = &arg.name;
let arg_type: u32 = arg.arg_type.into();
let key_spec_index = arg
.key_spec_index
.map(|v| quote! {Some(#v)})
.unwrap_or(quote! {None});
let token = to_token_stream(arg.token.clone());
let summary = to_token_stream(arg.summary.clone());
let since = to_token_stream(arg.since.clone());
let flags: Vec<&'static str> = arg
.flags
.as_ref()
.map(|v| v.iter().map(|v| v.into()).collect())
.unwrap_or_default();
let flags = quote! {
vec![#(redis_module::commands::CommandArgFlags::try_from(#flags)?, )*]
};
let deprecated_since = to_token_stream(arg.deprecated_since.clone());
let display_text = to_token_stream(arg.display_text.clone());

let subargs = if let Some(subargs_vec) = &arg.subargs {
let subargs_tokens: Vec<_> = subargs_vec.iter().map(generate_command_arg).collect();
quote! {
Some(vec![#(#subargs_tokens),*])
}
} else {
quote! { None }
};

quote! {
redis_module::commands::RedisModuleCommandArg::new(
#name.to_owned(),
#arg_type,
#key_spec_index,
#token,
#summary,
#since,
#flags.into(),
#deprecated_since,
#subargs,
#display_text,
)
}
}

pub(crate) fn redis_command(attr: TokenStream, item: TokenStream) -> TokenStream {
let args = parse_macro_input!(attr as Args);
let func: ItemFn = match syn::parse(item) {
Expand Down Expand Up @@ -358,6 +469,24 @@ pub(crate) fn redis_command(attr: TokenStream, item: TokenStream) -> TokenStream
})
.collect();

let command_args: Vec<_> = args
.args
.as_ref()
.map(|v| v.iter().map(generate_command_arg).collect())
.unwrap_or_default();

let acl_categories = args
.acl_categories
.map(|v| v.into_iter().map(String::from).collect::<Vec<_>>());

let acl_categories_tokens = if let Some(categories) = &acl_categories {
quote! {
Some(vec![#(#categories.to_owned()),*])
}
} else {
quote! { None }
};

let gen = quote! {
#func

Expand Down Expand Up @@ -385,6 +514,7 @@ pub(crate) fn redis_command(attr: TokenStream, item: TokenStream) -> TokenStream
),
)*
];
let command_args = vec![#(#command_args),*];
Ok(redis_module::commands::CommandInfo::new(
#name_literal.to_owned(),
Some(#flags_literal.to_owned()),
Expand All @@ -396,6 +526,8 @@ pub(crate) fn redis_command(attr: TokenStream, item: TokenStream) -> TokenStream
#arity_literal,
key_spec,
#c_function_name,
command_args,
#acl_categories_tokens,
))
}
};
Expand Down
Loading
Loading