1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
7#[serde(rename_all = "snake_case")]
8pub enum TurnPolicy {
9 #[default]
11 RoundRobin,
12 AdjacencyPair,
14}
15
16#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
18#[serde(rename_all = "snake_case")]
19pub enum DurationPolicy {
20 #[default]
22 SingleExchange,
23 MultiBeat {
25 max_beats: usize,
27 },
28 UntilResolved,
30}
31
32#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
34pub struct PracticeSpec {
35 pub name: String,
37 pub affordances: Vec<String>,
39 #[serde(default)]
41 pub turn_policy: TurnPolicy,
42 #[serde(default)]
44 pub duration_policy: DurationPolicy,
45 #[serde(default)]
47 pub entry_condition_source: String,
48}
49
50#[cfg(test)]
51mod tests {
52 use super::*;
53
54 #[test]
55 fn practice_spec_deserializes() {
56 let s = r#"
57 name = "negotiation"
58 affordances = ["offer", "counter_offer", "accept", "reject"]
59 turn_policy = "adjacency_pair"
60
61 [duration_policy]
62 multi_beat = { max_beats = 10 }
63 "#;
64
65 let spec: PracticeSpec = toml::from_str(s).expect("should deserialize");
66
67 assert_eq!(spec.name, "negotiation");
68 assert_eq!(spec.affordances.len(), 4);
69 assert_eq!(spec.turn_policy, TurnPolicy::AdjacencyPair);
70 assert_eq!(
71 spec.duration_policy,
72 DurationPolicy::MultiBeat { max_beats: 10 }
73 );
74 }
75
76 #[test]
77 fn practice_defaults_to_single_exchange_round_robin() {
78 let s = r#"
79 name = "greeting"
80 affordances = ["wave", "nod"]
81 "#;
82
83 let spec: PracticeSpec = toml::from_str(s).expect("should deserialize");
84
85 assert_eq!(spec.name, "greeting");
86 assert_eq!(spec.affordances.len(), 2);
87 assert_eq!(spec.turn_policy, TurnPolicy::RoundRobin);
88 assert_eq!(spec.duration_policy, DurationPolicy::SingleExchange);
89 assert_eq!(spec.entry_condition_source, "");
90 }
91}