123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223 |
- //! # Macros
- //!
- //! All macros are defined in this module
- /// Dispatcher macro
- ///
- /// In this macro all command and their definition are defined.
- ///
- /// This macro generates rust code for each command and encapsulates all the logic to fast
- /// execution.
- ///
- /// Using macros allow to generate pretty efficient code for run time and easy to extend at
- /// writting time.
- #[macro_export]
- macro_rules! dispatcher {
- {
- $($ns:ident {
- $($command:ident {
- $handler:expr,
- [$($tag:tt)+],
- $min_args:expr,
- $key_start:expr,
- $key_stop:expr,
- $key_step:expr,
- $is_queueable:expr,
- }),+$(,)?
- }),+$(,)?
- }=> {
- use futures::future::FutureExt;
- /// Metrics for all defined commands
- #[derive(serde::Serialize)]
- pub struct ServiceMetricRegistry<'a> {
- $($(
- $command: &'a command::Metrics,
- )+)+
- }
- /// Dispatcher struct
- ///
- /// The list of commands are generated in this strucutre by this macro.
- #[allow(non_snake_case, non_camel_case_types)]
- #[derive(Debug)]
- pub struct Dispatcher {
- $($(
- $command: command::Command,
- )+)+
- }
- impl Dispatcher {
- /// Creates a new dispatcher.
- pub fn new() -> Self {
- Self {
- $($(
- $command: command::Command::new(
- stringify!($command),
- stringify!($ns),
- &[$($tag,)+],
- $min_args,
- $key_start,
- $key_stop,
- $key_step,
- $is_queueable,
- ),
- )+)+
- }
- }
- /// Returns all metrics objects
- pub fn get_service_metric_registry(&self) -> ServiceMetricRegistry {
- ServiceMetricRegistry {
- $($(
- $command: self.$command.metrics(),
- )+)+
- }
- }
- /// Returns the handlers for defined commands.
- pub fn get_all_commands(&self) -> Vec<&command::Command> {
- vec![
- $($(
- &self.$command,
- )+)+
- ]
- }
- /// Returns a command handler for a given command
- #[inline(always)]
- pub fn get_handler_for_command(&self, command: &str) -> Result<&command::Command, Error> {
- match command.to_lowercase().as_str() {
- $($(
- stringify!($command) => Ok(&self.$command),
- )+)+
- _ => Err(Error::CommandNotFound(command.into())),
- }
- }
- /// Returns the command handler
- ///
- /// Before returning the command handler this function will make sure the minimum
- /// required arguments are provided. This pre-validation ensures each command handler
- /// has fewer logic when reading the provided arguments.
- #[inline(always)]
- pub fn get_handler(&self, args: &[Bytes]) -> Result<&command::Command, Error> {
- let command = String::from_utf8_lossy(&args[0]).to_lowercase();
- let command = self.get_handler_for_command(&command)?;
- if ! command.check_number_args(args.len()) {
- Err(Error::InvalidArgsCount(command.name().into()))
- } else {
- Ok(command)
- }
- }
- /// Returns the command handler
- ///
- /// Before returning the command handler this function will make sure the minimum
- /// required arguments are provided. This pre-validation ensures each command handler
- /// has fewer logic when reading the provided arguments.
- #[inline(always)]
- pub fn execute<'a>(&'a self, conn: &'a Connection, args: &'a [Bytes]) -> futures::future::BoxFuture<'a, Result<Value, Error>> {
- async move {
- let command = String::from_utf8_lossy(&args[0]);
- match command.to_lowercase().as_str() {
- $($(
- stringify!($command) => {
- let command = &self.$command;
- if ! command.check_number_args(args.len()) {
- Err(Error::InvalidArgsCount(command.name().into()))
- } else {
- let metrics = command.metrics();
- let hit_count = &metrics.hit_count;
- let error_count = &metrics.error_count;
- let in_flight = &metrics.in_flight;
- let response_time = &metrics.response_time;
- let throughput = &metrics.throughput;
- let status = conn.status();
- if status == ConnectionStatus::Multi && command.is_queueable() {
- conn.queue_command(args);
- conn.tx_keys(command.get_keys(args));
- return Ok(Value::Queued);
- } else if status == ConnectionStatus::Pubsub && ! command.is_pubsub_executable() {
- return Err(Error::PubsubOnly(stringify!($command).to_owned()));
- }
- metered::measure!(hit_count, {
- metered::measure!(response_time, {
- metered::measure!(throughput, {
- metered::measure!(in_flight, {
- metered::measure!(error_count, $handler(conn, args).await)
- })
- })
- })
- })
- }
- }
- )+)+,
- _ => Err(Error::CommandNotFound(command.into())),
- }
- }.boxed()
- }
- }
- }
- }
- /// Generate code for From/Into a type to a Value
- #[macro_export]
- macro_rules! value_try_from {
- {$type: ty, $value: expr} => {
- impl From<$type> for Value {
- fn from(value: $type) -> Value {
- $value(value.into())
- }
- }
- value_vec_try_from!($type);
- }
- }
- /// Generate code for From/Into a vec of type to a Value::Array
- #[macro_export]
- macro_rules! value_vec_try_from {
- {$type: ty} => {
- impl From<Vec<$type>> for Value {
- fn from(value: Vec<$type>) -> Value {
- Value::Array(value.iter().map(|x| (*x).into()).collect())
- }
- }
- }
- }
- /// Converts an Option<T> to Value. If the option is None Value::Null is returned.
- #[macro_export]
- macro_rules! option {
- {$type: expr} => {
- if let Some(val) = $type {
- val.into()
- } else {
- Value::Null
- }
- }
- }
- /// Check if a given command argument in a position $pos is eq to a $command
- #[macro_export]
- macro_rules! check_arg {
- {$args: tt, $pos: tt, $command: tt} => {{
- match $args.get($pos) {
- Some(bytes) => {
- String::from_utf8_lossy(&bytes).to_uppercase() == $command
- },
- None => false,
- }
- }}
- }
- /// Convert a stream to a Bytes
- #[macro_export]
- macro_rules! bytes {
- ($content:tt) => {
- Bytes::from(&$content[..])
- };
- }
|