action.rs

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