action.rs

  1use std::any::{Any, TypeId};
  2
  3pub trait Action: 'static {
  4    fn id(&self) -> TypeId;
  5    fn namespace(&self) -> &'static str;
  6    fn name(&self) -> &'static str;
  7    fn as_any(&self) -> &dyn Any;
  8    fn boxed_clone(&self) -> Box<dyn Action>;
  9    fn eq(&self, other: &dyn Action) -> bool;
 10
 11    fn qualified_name() -> &'static str
 12    where
 13        Self: Sized;
 14    fn from_json_str(json: &str) -> anyhow::Result<Box<dyn Action>>
 15    where
 16        Self: Sized;
 17}
 18
 19/// Define a set of unit struct types that all implement the `Action` trait.
 20///
 21/// The first argument is a namespace that will be associated with each of
 22/// the given action types, to ensure that they have globally unique
 23/// qualified names for use in keymap files.
 24#[macro_export]
 25macro_rules! actions {
 26    ($namespace:path, [ $($name:ident),* $(,)? ]) => {
 27        $(
 28            #[derive(Clone, Debug, Default, PartialEq, Eq)]
 29            pub struct $name;
 30            $crate::__impl_action! {
 31                $namespace,
 32                $name,
 33                fn from_json_str(_: &str) -> $crate::anyhow::Result<Box<dyn $crate::Action>> {
 34                    Ok(Box::new(Self))
 35                }
 36            }
 37        )*
 38    };
 39}
 40
 41/// Implement the `Action` trait for a set of existing types.
 42///
 43/// The first argument is a namespace that will be associated with each of
 44/// the given action types, to ensure that they have globally unique
 45/// qualified names for use in keymap files.
 46#[macro_export]
 47macro_rules! impl_actions {
 48    ($namespace:path, [ $($name:ident),* $(,)? ]) => {
 49        $(
 50            $crate::__impl_action! {
 51                $namespace,
 52                $name,
 53                fn from_json_str(json: &str) -> $crate::anyhow::Result<Box<dyn $crate::Action>> {
 54                    Ok(Box::new($crate::serde_json::from_str::<Self>(json)?))
 55                }
 56            }
 57        )*
 58    };
 59}
 60
 61/// Implement the `Action` trait for a set of existing types that are
 62/// not intended to be constructed via a keymap file, but only dispatched
 63/// internally.
 64#[macro_export]
 65macro_rules! impl_internal_actions {
 66    ($namespace:path, [ $($name:ident),* $(,)? ]) => {
 67        $(
 68            $crate::__impl_action! {
 69                $namespace,
 70                $name,
 71                fn from_json_str(_: &str) -> $crate::anyhow::Result<Box<dyn $crate::Action>> {
 72                    Err($crate::anyhow::anyhow!("internal action"))
 73                }
 74            }
 75        )*
 76    };
 77}
 78
 79#[doc(hidden)]
 80#[macro_export]
 81macro_rules! __impl_action {
 82    ($namespace:path, $name:ident, $from_json_fn:item) => {
 83        impl $crate::action::Action for $name {
 84            fn namespace(&self) -> &'static str {
 85                stringify!($namespace)
 86            }
 87
 88            fn name(&self) -> &'static str {
 89                stringify!($name)
 90            }
 91
 92            fn qualified_name() -> &'static str {
 93                concat!(
 94                    stringify!($namespace),
 95                    "::",
 96                    stringify!($name),
 97                )
 98            }
 99
100            fn id(&self) -> std::any::TypeId {
101                std::any::TypeId::of::<$name>()
102            }
103
104            fn as_any(&self) -> &dyn std::any::Any {
105                self
106            }
107
108            fn boxed_clone(&self) -> Box<dyn $crate::Action> {
109                Box::new(self.clone())
110            }
111
112            fn eq(&self, other: &dyn $crate::Action) -> bool {
113                if let Some(other) = other.as_any().downcast_ref::<Self>() {
114                    self == other
115                } else {
116                    false
117                }
118            }
119
120            $from_json_fn
121        }
122    };
123}