pub trait Action: Sized {
type State: State;
}Expand description
The set of atomic changes which can be made to the State objects.
Actions consist of Create, Update, and Destroy types. To define a type as an action, derive one of these traits for it, and implement the trait (the derive implements a wrapper to unify Create/Update/Destroy under a single internal trait).
§Implementing Create/Update/Destroy
Each of these traits has a type parameter T, indicating the type of State it operates on.
Type parameter Undo specifies a type which can be used to revert the item to its previous state.
For Create, this will be a Destroy, and vice versa. For Update, this will be an Update
(possibly even Self, depending on the action). If an action is not successful, the effective
game state must be unchanged, and the action returns an error. See Create, Update, and Destroy
for more details.
Here’s an example of an Update action that adds a value to an i32 item:
// Both `derive` and `impl` are required
#[derive(spru::action::Update)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct AddI32(pub i32);
impl spru::action::Update for AddI32 {
// This action applies to a i32 value
type T = i32;
// We can reuse the same action type by negating the value when undoing
type Undo = AddI32;
fn update(&self, value: &mut Self::T) -> spru::common::error::AnyResult<impl Into<Option<Self::Undo>>> {
// Note: This example is not overflow-safe
// An update modifies an existing `value`
*value += self.0;
// Adding the negated value effectively undoes it
Ok(Some(AddI32(-self.0)))
}
}§cloned Actions
The crate spru_util contains cloned::Create<T>, cloned::Update<T>, and cloned::Destroy<T>.
These actions modify the item through serialization. This is usually all you need to Create and Destroy,
but may be inefficient or semantically lacking for updates.
§Implementing Action
Action is implemented using tagset::tagset to create a tagged union of all possible actions. You must specify the State set this action set applies to:
#[tagset(impl spru::Action {
type State = MyState;
})]Here is an example of a game with a State set of { MyObject }, and Actions { MyCreate, MyDestroy }:
#[derive(serde::Serialize, serde::Deserialize)]
pub struct MyObject(i32);
#[tagset(impl tagset::proxy::serde::Serialize)]
#[tagset(impl<'de> tagset::serde::DeserializeFromDiscriminant<'de>)]
#[tagset(impl<'de> tagset::proxy::serde::Deserialize<'de>)]
#[tagset(impl spru::State)]
#[tagset(MyObject)]
pub struct MyState;
#[derive(spru::action::Create)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct MyCreate {
value: i32,
}
impl spru::action::Create for MyCreate {
type T = MyObject;
type Undo = MyDestroy;
fn create(&self) -> spru::common::error::AnyResult<(Self::T, Self::Undo)> {
Ok((MyObject(self.value), MyDestroy { }))
}
}
#[derive(spru::action::Destroy)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct MyDestroy;
impl spru::action::Destroy for MyDestroy {
type T = MyObject;
type Undo = MyCreate;
fn destroy(&self, value: Self::T) -> spru::common::error::AnyResult<Self::Undo> {
Ok(MyCreate { value: value.0 })
}
}
#[tagset(impl spru::Action {
type State = MyState;
})]
#[tagset(impl tagset::proxy::serde::Serialize)]
#[tagset(impl<'de> tagset::serde::DeserializeFromDiscriminant<'de>)]
#[tagset(impl<'de> tagset::proxy::serde::Deserialize<'de>)]
#[tagset(derive(Debug, Clone))]
#[tagset(MyCreate)]
#[tagset(MyDestroy)]
pub struct MyAction;
Required Associated Types§
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.