131 lines
3.8 KiB
Rust
131 lines
3.8 KiB
Rust
use std::any::{Any, TypeId};
|
|
|
|
pub trait Action: 'static {
|
|
fn id(&self) -> TypeId;
|
|
fn namespace(&self) -> &'static str;
|
|
fn name(&self) -> &'static str;
|
|
fn as_any(&self) -> &dyn Any;
|
|
fn boxed_clone(&self) -> Box<dyn Action>;
|
|
fn eq(&self, other: &dyn Action) -> bool;
|
|
|
|
fn qualified_name() -> &'static str
|
|
where
|
|
Self: Sized;
|
|
fn from_json_str(json: &str) -> anyhow::Result<Box<dyn Action>>
|
|
where
|
|
Self: Sized;
|
|
}
|
|
|
|
impl std::fmt::Debug for dyn Action {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
f.debug_struct("dyn Action")
|
|
.field("namespace", &self.namespace())
|
|
.field("name", &self.name())
|
|
.finish()
|
|
}
|
|
}
|
|
/// Define a set of unit struct types that all implement the `Action` trait.
|
|
///
|
|
/// The first argument is a namespace that will be associated with each of
|
|
/// the given action types, to ensure that they have globally unique
|
|
/// qualified names for use in keymap files.
|
|
#[macro_export]
|
|
macro_rules! actions {
|
|
($namespace:path, [ $($name:ident),* $(,)? ]) => {
|
|
$(
|
|
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
|
pub struct $name;
|
|
$crate::__impl_action! {
|
|
$namespace,
|
|
$name,
|
|
fn from_json_str(_: &str) -> $crate::anyhow::Result<Box<dyn $crate::Action>> {
|
|
Ok(Box::new(Self))
|
|
}
|
|
}
|
|
)*
|
|
};
|
|
}
|
|
|
|
/// Implement the `Action` trait for a set of existing types.
|
|
///
|
|
/// The first argument is a namespace that will be associated with each of
|
|
/// the given action types, to ensure that they have globally unique
|
|
/// qualified names for use in keymap files.
|
|
#[macro_export]
|
|
macro_rules! impl_actions {
|
|
($namespace:path, [ $($name:ident),* $(,)? ]) => {
|
|
$(
|
|
$crate::__impl_action! {
|
|
$namespace,
|
|
$name,
|
|
fn from_json_str(json: &str) -> $crate::anyhow::Result<Box<dyn $crate::Action>> {
|
|
Ok(Box::new($crate::serde_json::from_str::<Self>(json)?))
|
|
}
|
|
}
|
|
)*
|
|
};
|
|
}
|
|
|
|
/// Implement the `Action` trait for a set of existing types that are
|
|
/// not intended to be constructed via a keymap file, but only dispatched
|
|
/// internally.
|
|
#[macro_export]
|
|
macro_rules! impl_internal_actions {
|
|
($namespace:path, [ $($name:ident),* $(,)? ]) => {
|
|
$(
|
|
$crate::__impl_action! {
|
|
$namespace,
|
|
$name,
|
|
fn from_json_str(_: &str) -> $crate::anyhow::Result<Box<dyn $crate::Action>> {
|
|
Err($crate::anyhow::anyhow!("internal action"))
|
|
}
|
|
}
|
|
)*
|
|
};
|
|
}
|
|
|
|
#[doc(hidden)]
|
|
#[macro_export]
|
|
macro_rules! __impl_action {
|
|
($namespace:path, $name:ident, $from_json_fn:item) => {
|
|
impl $crate::action::Action for $name {
|
|
fn namespace(&self) -> &'static str {
|
|
stringify!($namespace)
|
|
}
|
|
|
|
fn name(&self) -> &'static str {
|
|
stringify!($name)
|
|
}
|
|
|
|
fn qualified_name() -> &'static str {
|
|
concat!(
|
|
stringify!($namespace),
|
|
"::",
|
|
stringify!($name),
|
|
)
|
|
}
|
|
|
|
fn id(&self) -> std::any::TypeId {
|
|
std::any::TypeId::of::<$name>()
|
|
}
|
|
|
|
fn as_any(&self) -> &dyn std::any::Any {
|
|
self
|
|
}
|
|
|
|
fn boxed_clone(&self) -> Box<dyn $crate::Action> {
|
|
Box::new(self.clone())
|
|
}
|
|
|
|
fn eq(&self, other: &dyn $crate::Action) -> bool {
|
|
if let Some(other) = other.as_any().downcast_ref::<Self>() {
|
|
self == other
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
|
|
$from_json_fn
|
|
}
|
|
};
|
|
}
|