Skip to main content

plugin/
plugin.rs

1use canwu_api::{
2    ArmyId, Canwu, CanwuError, Command, CommandContext, CommandEnvelope, EntityRef, ErrorCode,
3    Issuer, PayloadProperty, PayloadSchema, PayloadValueType, PluginActionDescriptor,
4    PluginRegistrar, SimulationPlugin, SimulationView, StateKey, SystemDirective,
5};
6use serde_json::{Value, json};
7use std::collections::BTreeMap;
8
9struct StancePlugin;
10
11fn set_stance(
12    view: &SimulationView<'_>,
13    context: &CommandContext,
14    payload: &Value,
15) -> Result<Vec<SystemDirective>, CanwuError> {
16    let army = ArmyId::new(
17        payload
18            .get("army")
19            .and_then(Value::as_u64)
20            .expect("payload was validated before the handler ran"),
21    );
22    let Some(army_state) = view.army(army)? else {
23        return Err(CanwuError::new(
24            ErrorCode::ArmyNotFound,
25            format!("army {army} was not found"),
26        ));
27    };
28    if context.issuer != Issuer::Actor(army_state.commander) {
29        return Err(CanwuError::new(
30            ErrorCode::InvalidAuthority,
31            "only the army commander may set its stance",
32        ));
33    }
34    Ok(vec![SystemDirective::SetComponent {
35        state: StateKey::new("military", "stance"),
36        entity: EntityRef::Army(army),
37        component: "stance".to_owned(),
38        value: payload["stance"].clone(),
39        summary: format!("Army {army} changed stance"),
40    }])
41}
42
43impl SimulationPlugin for StancePlugin {
44    fn name(&self) -> &'static str {
45        "example-stance"
46    }
47
48    fn version(&self) -> &'static str {
49        "1.0.0"
50    }
51
52    fn semantic_hash(&self) -> &'static str {
53        "a33fb7c59ae5a17685bd94f99154c5423dc7bf70d3dbde6ef4daf73670a37d26"
54    }
55
56    fn register(&self, registrar: &mut PluginRegistrar<'_>) -> Result<(), CanwuError> {
57        registrar.register_command(
58            PluginActionDescriptor {
59                name: "set_stance".to_owned(),
60                description: "Set an army stance through an issuer-aware command".to_owned(),
61                payload_schema: PayloadSchema::Object {
62                    properties: BTreeMap::from([
63                        (
64                            "army".to_owned(),
65                            PayloadProperty {
66                                value_type: PayloadValueType::Integer,
67                                required: true,
68                            },
69                        ),
70                        (
71                            "stance".to_owned(),
72                            PayloadProperty {
73                                value_type: PayloadValueType::String,
74                                required: true,
75                            },
76                        ),
77                    ]),
78                    allow_additional: false,
79                },
80                reads: vec![StateKey::core_armies()],
81                writes: vec![StateKey::new("military", "stance")],
82            },
83            set_stance,
84        )
85    }
86}
87
88fn main() -> Result<(), CanwuError> {
89    let mut canwu = Canwu::demo(35)?;
90    let ids = Canwu::demo_ids();
91    canwu.register_plugin(&StancePlugin)?;
92    canwu.submit(CommandEnvelope::new(
93        Issuer::Actor(ids.commander),
94        Command::Plugin {
95            plugin: "example-stance".to_owned(),
96            command: "set_stance".to_owned(),
97            payload: json!({ "army": ids.army, "stance": "hold" }),
98        },
99    ))?;
100    Ok(())
101}