action.rs

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