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}