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