Skip to main content

pflow_tokenmodel/
schema.rs

1//! Token model schema types.
2
3use std::collections::HashMap;
4
5use serde::{Deserialize, Serialize};
6
7/// Discriminates between token-counting and data-holding states.
8#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
9#[serde(rename_all = "lowercase")]
10pub enum Kind {
11    Token,
12    Data,
13}
14
15impl Default for Kind {
16    fn default() -> Self {
17        Kind::Data
18    }
19}
20
21/// A named container in a schema.
22#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct State {
24    pub id: String,
25
26    #[serde(default, skip_serializing_if = "is_default_kind")]
27    pub kind: Kind,
28
29    #[serde(default, skip_serializing_if = "Option::is_none")]
30    pub initial: Option<serde_json::Value>,
31
32    #[serde(default, skip_serializing_if = "String::is_empty")]
33    #[serde(rename = "type")]
34    pub typ: String,
35
36    #[serde(default, skip_serializing_if = "is_false")]
37    pub exported: bool,
38}
39
40fn is_default_kind(k: &Kind) -> bool {
41    *k == Kind::Data
42}
43
44fn is_false(b: &bool) -> bool {
45    !b
46}
47
48impl State {
49    pub fn is_token(&self) -> bool {
50        self.kind == Kind::Token
51    }
52
53    pub fn is_data(&self) -> bool {
54        self.kind == Kind::Data
55    }
56
57    /// Returns the initial token count (for TokenState).
58    pub fn initial_tokens(&self) -> i64 {
59        if !self.is_token() {
60            return 0;
61        }
62        match &self.initial {
63            Some(v) => v.as_i64().unwrap_or(0),
64            None => 0,
65        }
66    }
67}
68
69/// A state-changing operation.
70#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct Action {
72    pub id: String,
73
74    #[serde(default, skip_serializing_if = "String::is_empty")]
75    pub guard: String,
76
77    #[serde(default, skip_serializing_if = "String::is_empty")]
78    pub event_id: String,
79
80    #[serde(default, skip_serializing_if = "Option::is_none")]
81    pub event_bindings: Option<HashMap<String, String>>,
82}
83
84/// An arc connecting states and actions.
85#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct Arc {
87    pub source: String,
88    pub target: String,
89
90    #[serde(default, skip_serializing_if = "Vec::is_empty")]
91    pub keys: Vec<String>,
92
93    #[serde(default, skip_serializing_if = "String::is_empty")]
94    pub value: String,
95}
96
97/// A constraint that must hold across all snapshots.
98#[derive(Debug, Clone, Serialize, Deserialize)]
99pub struct Constraint {
100    pub id: String,
101    pub expr: String,
102}
103
104/// An Ethereum event that can trigger state changes.
105#[derive(Debug, Clone, Serialize, Deserialize)]
106pub struct Event {
107    pub id: String,
108    pub signature: String,
109
110    #[serde(default, skip_serializing_if = "String::is_empty")]
111    pub topic: String,
112
113    #[serde(default)]
114    pub parameters: Vec<EventParameter>,
115}
116
117/// A single parameter in an event signature.
118#[derive(Debug, Clone, Serialize, Deserialize)]
119pub struct EventParameter {
120    pub name: String,
121
122    #[serde(rename = "type")]
123    pub typ: String,
124
125    #[serde(default, skip_serializing_if = "is_false")]
126    pub indexed: bool,
127}
128
129/// A complete tokenmodel schema definition.
130#[derive(Debug, Clone, Serialize, Deserialize)]
131pub struct Schema {
132    #[serde(default, skip_serializing_if = "String::is_empty")]
133    pub name: String,
134
135    #[serde(default, skip_serializing_if = "String::is_empty")]
136    pub version: String,
137
138    pub states: Vec<State>,
139    pub actions: Vec<Action>,
140    pub arcs: Vec<Arc>,
141
142    #[serde(default, skip_serializing_if = "Vec::is_empty")]
143    pub constraints: Vec<Constraint>,
144
145    #[serde(default, skip_serializing_if = "Vec::is_empty")]
146    pub events: Vec<Event>,
147}
148
149impl Schema {
150    pub fn new(name: impl Into<String>) -> Self {
151        Self {
152            name: name.into(),
153            version: "1.0.0".into(),
154            states: Vec::new(),
155            actions: Vec::new(),
156            arcs: Vec::new(),
157            constraints: Vec::new(),
158            events: Vec::new(),
159        }
160    }
161
162    pub fn add_state(&mut self, st: State) -> &mut Self {
163        self.states.push(st);
164        self
165    }
166
167    pub fn add_token_state(&mut self, id: impl Into<String>, initial: i64) -> &mut Self {
168        self.states.push(State {
169            id: id.into(),
170            kind: Kind::Token,
171            initial: Some(serde_json::Value::Number(initial.into())),
172            typ: "int".into(),
173            exported: false,
174        });
175        self
176    }
177
178    pub fn add_data_state(
179        &mut self,
180        id: impl Into<String>,
181        typ: impl Into<String>,
182        initial: Option<serde_json::Value>,
183        exported: bool,
184    ) -> &mut Self {
185        self.states.push(State {
186            id: id.into(),
187            kind: Kind::Data,
188            typ: typ.into(),
189            initial,
190            exported,
191        });
192        self
193    }
194
195    pub fn add_action(&mut self, a: Action) -> &mut Self {
196        self.actions.push(a);
197        self
198    }
199
200    pub fn add_arc(&mut self, a: Arc) -> &mut Self {
201        self.arcs.push(a);
202        self
203    }
204
205    pub fn add_constraint(&mut self, c: Constraint) -> &mut Self {
206        self.constraints.push(c);
207        self
208    }
209
210    pub fn add_event(&mut self, e: Event) -> &mut Self {
211        self.events.push(e);
212        self
213    }
214
215    pub fn state_by_id(&self, id: &str) -> Option<&State> {
216        self.states.iter().find(|s| s.id == id)
217    }
218
219    pub fn action_by_id(&self, id: &str) -> Option<&Action> {
220        self.actions.iter().find(|a| a.id == id)
221    }
222
223    pub fn event_by_id(&self, id: &str) -> Option<&Event> {
224        self.events.iter().find(|e| e.id == id)
225    }
226
227    pub fn action_for_event(&self, event_id: &str) -> Option<&Action> {
228        self.actions.iter().find(|a| a.event_id == event_id)
229    }
230
231    /// Returns all arcs flowing into an action.
232    pub fn input_arcs(&self, action_id: &str) -> Vec<&Arc> {
233        self.arcs.iter().filter(|a| a.target == action_id).collect()
234    }
235
236    /// Returns all arcs flowing out of an action.
237    pub fn output_arcs(&self, action_id: &str) -> Vec<&Arc> {
238        self.arcs.iter().filter(|a| a.source == action_id).collect()
239    }
240
241    pub fn token_states(&self) -> Vec<&State> {
242        self.states.iter().filter(|s| s.is_token()).collect()
243    }
244
245    pub fn data_states(&self) -> Vec<&State> {
246        self.states.iter().filter(|s| s.is_data()).collect()
247    }
248}