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