pflow_metamodel/
lib.rs

1//! > **Metamodel - A Rust Library for Abstract State Machine Modeling**
2//!
3//! See the code below for an example of how to create a Petri-net model using the `pflow_metamodel` library.
4//!
5//! Also see: [Macro pflow_metamodel::pflow_dsl](macro.pflow_dsl.html) for creating models using an internal rust DSL rather than json.
6//! ```
7//! use pflow_metamodel::*;
8//!
9//! let model: Model = pflow_json!{{
10//!    "modelType": "petriNet",
11//!    "version": "v0",
12//!    "places": {
13//!      "place0": { "offset": 0, "capacity": 3, "x": 100, "y": 180 }
14//!    },
15//!    "transitions": {
16//!      "txn0": { "role": "role0", "x": 20, "y": 100 },
17//!      "txn1": { "role": "role0", "x": 180, "y": 100 },
18//!      "txn2": { "role": "role0", "x": 20, "y": 260 },
19//!      "txn3": { "role": "role0", "x": 180, "y": 260 }
20//!    },
21//!    "arcs": [
22//!      { "source": "txn0", "target": "place0" },
23//!      { "source": "place0", "target": "txn1", "weight": 3 },
24//!      { "source": "txn2", "target": "place0", "weight": 3, "inhibit": true },
25//!      { "source": "place0", "target": "txn3", "inhibit": true }
26//!    ]
27//! }};
28//!
29//! let state = model.vm.initial_vector();
30//! assert_eq!(state, vec![0]);
31//! let res = model.vm.transform(&state, "txn0", 1);
32//! assert!(res.ok);
33//! assert_eq!(state, vec![0]); // input state is _not_ mutated
34//! assert_eq!(res.output, vec![1]);
35//! let t = model.net.transitions.get("txn0");
36//! assert!(t.is_some());
37//! ```
38//!
39//! - Provides a DSL-driven framework for modeling and simulating Petri-nets, wf-nets, and DFAs.
40//! - State machine data types are executed as a [Vector Addition State Machine (VASM)](https://en.wikipedia.org/wiki/Vector_addition_system).
41//! - Data models are viewable / shareable in browsers by using [https://pflow.dev](https://pflow.dev/p/zb2rhkizUC1o2JuvgwhbH1XrLZkdK8x66pP1KR7sWAEw9c5FE/) JSON format.
42//! - Models can be compressed and shared as base64 encoded blobs.
43//! - Models can be consistently hashed and shared as CIDs.
44//!
45//! ![pflow][pflow]
46//!
47//! [pflow]: data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyODAiIGhlaWdodD0iMjgwIiB2aWV3Qm94PSItMzggNDIgMjgwIDI4MCI+CjxkZWZzPjxtYXJrZXIgaWQ9Im1hcmtlckFycm93MSIgbWFya2VyV2lkdGg9IjIzIiBtYXJrZXJIZWlnaHQ9IjEzIiByZWZYPSIzMSIgcmVmWT0iNiIgb3JpZW50PSJhdXRvIj48cmVjdCB3aWR0aD0iMjgiIGhlaWdodD0iMyIgZmlsbD0id2hpdGUiIHN0cm9rZT0id2hpdGUiIHg9IjMiIHk9IjUiLz48cGF0aCBkPSJNMiwyIEwyLDExIEwxMCw2IEwyLDIiLz48L21hcmtlcj48bWFya2VyIGlkPSJtYXJrZXJJbmhpYml0MSIgbWFya2VyV2lkdGg9IjIzIiBtYXJrZXJIZWlnaHQ9IjEzIiByZWZYPSIzMSIgcmVmWT0iNiIgb3JpZW50PSJhdXRvIj48cmVjdCB3aWR0aD0iMjgiIGhlaWdodD0iMyIgZmlsbD0id2hpdGUiIHN0cm9rZT0id2hpdGUiIHg9IjMiIHk9IjUiLz48Y2lyY2xlIGN4PSI1IiBjeT0iNi41IiByPSI0Ii8+PC9tYXJrZXI+PC9kZWZzPgo8Zz4KPGxpbmUgeDE9IjIyIiB5MT0iMTAyIiB4Mj0iOTkiIHkyPSIxODMiIHN0cm9rZT0iIzAwMDAwMCIgbWFya2VyLWVuZD0idXJsKCNtYXJrZXJBcnJvdzEpIiAvPgo8dGV4dCB4PSI1NiIgeT0iMTM4IiBmb250LXNpemU9InNtYWxsIj4xPC90ZXh0Pgo8L2c+CjxnPgo8bGluZSB4MT0iOTkiIHkxPSIxODMiIHgyPSIxODIiIHkyPSIxMDIiIHN0cm9rZT0iIzAwMDAwMCIgbWFya2VyLWVuZD0idXJsKCNtYXJrZXJBcnJvdzEpIiAvPgo8dGV4dCB4PSIxMzYiIHk9IjEzOCIgZm9udC1zaXplPSJzbWFsbCI+MzwvdGV4dD4KPC9nPgo8Zz4KPGxpbmUgeDE9IjIyIiB5MT0iMjYyIiB4Mj0iOTkiIHkyPSIxODMiIHN0cm9rZT0iIzAwMDAwMCIgbWFya2VyLWVuZD0idXJsKCNtYXJrZXJJbmhpYml0MSkiIC8+Cjx0ZXh0IHg9IjU2IiB5PSIyMTgiIGZvbnQtc2l6ZT0ic21hbGwiPjM8L3RleHQ+CjwvZz4KPGc+CjxsaW5lIHgxPSI5OSIgeTE9IjE4MyIgeDI9IjE4MiIgeTI9IjI2MiIgc3Ryb2tlPSIjMDAwMDAwIiBtYXJrZXItZW5kPSJ1cmwoI21hcmtlckluaGliaXQxKSIgLz4KPHRleHQgeD0iMTM2IiB5PSIyMTgiIGZvbnQtc2l6ZT0ic21hbGwiPjE8L3RleHQ+CjwvZz4KPGc+CjxjaXJjbGUgY3g9Ijk5IiBjeT0iMTgzIiByPSIxNiIgc3Ryb2tlV2lkdGg9IjEuNSIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjMDAwMDAwIiBvcmllbnQ9IjAiIHNoYXBlUmVuZGVyaW5nPSJhdXRvIiAvPjx0ZXh0IHg9IjgxIiB5PSIxNjMiIGZvbnQtc2l6ZT0ic21hbGwiPnBsYWNlMDwvdGV4dD4KPC9nPgo8Zz4KPHJlY3QgeD0iMTY1IiB5PSI4NSIgd2lkdGg9IjMwIiBoZWlnaHQ9IjMwIiBzdHJva2U9IiMwMDAwMDAiIGZpbGw9IiNmZmZmZmYiIHJ4PSI0IiAvPjx0ZXh0IHg9IjE2NSIgeT0iNzciIGZvbnQtc2l6ZT0ic21hbGwiPnR4bjE8L3RleHQ+CjwvZz4KPGc+CjxyZWN0IHg9IjUiIHk9IjI0NSIgd2lkdGg9IjMwIiBoZWlnaHQ9IjMwIiBzdHJva2U9IiMwMDAwMDAiIGZpbGw9IiNmYWI1YjAiIHJ4PSI0IiAvPjx0ZXh0IHg9IjUiIHk9IjIzNyIgZm9udC1zaXplPSJzbWFsbCI+dHhuMjwvdGV4dD4KPC9nPgo8Zz4KPHJlY3QgeD0iMTY1IiB5PSIyNDUiIHdpZHRoPSIzMCIgaGVpZ2h0PSIzMCIgc3Ryb2tlPSIjMDAwMDAwIiBmaWxsPSIjNjJmYTc1IiByeD0iNCIgLz48dGV4dCB4PSIxNjUiIHk9IjIzNyIgZm9udC1zaXplPSJzbWFsbCI+dHhuMzwvdGV4dD4KPC9nPgo8Zz4KPHJlY3QgeD0iNSIgeT0iODUiIHdpZHRoPSIzMCIgaGVpZ2h0PSIzMCIgc3Ryb2tlPSIjMDAwMDAwIiBmaWxsPSIjNjJmYTc1IiByeD0iNCIgLz48dGV4dCB4PSI1IiB5PSI3NyIgZm9udC1zaXplPSJzbWFsbCI+dHhuMDwvdGV4dD4KPC9nPgo8L3N2Zz4=
48//!
49//! # Dining Philosophers Example
50//!
51//! The following example demonstrates the dining philosophers problem using the `PetriNet` struct.
52//!
53//! - read more about the [dining philosophers problem](https://pflow.dev/examples-dining-philosophers).
54//! - interact with the model [dining philosophers model](https://pflow.dev/p/zb2rhimQLDKMY6yBXMLV2DJyCPqseb9kTJKdjiwKgzQEgwGvt/)
55//! ```rust
56//! use pflow_metamodel::*;
57//!
58//! let model = pflow_json!{{
59//! "modelType": "petriNet",
60//! "version": "v0",
61//! "places": {
62//!   "chopstick0": { "offset": 0, "initial": 1, "x": 403, "y": 340 },
63//!   "chopstick1": { "offset": 1, "initial": 1, "x": 534, "y": 345 },
64//!   "chopstick2": { "offset": 2, "initial": 1, "x": 358, "y": 467 },
65//!   "chopstick3": { "offset": 3, "initial": 1, "x": 547, "y": 461 },
66//!   "chopstick4": { "offset": 4, "initial": 1, "x": 451, "y": 536 },
67//!   "0right": { "offset": 5, "x": 415, "y": 181 },
68//!   "0left": { "offset": 6, "x": 545, "y": 177 },
69//!   "1right": { "offset": 7, "x": 719, "y": 288 },
70//!   "1left": { "offset": 8, "x": 769, "y": 404 },
71//!   "2left": { "offset": 9, "x": 686, "y": 584 },
72//!   "2right": { "offset": 10, "x": 594, "y": 678 },
73//!   "3left": { "offset": 11, "x": 315, "y": 679 },
74//!   "3right": { "offset": 12, "x": 216, "y": 608 },
75//!   "4right": { "offset": 13, "x": 148, "y": 397 },
76//!   "4left": { "offset": 14, "x": 183, "y": 289 }
77//! },
78//! "transitions": {
79//!   "0think": { "x": 478, "y": 106 },
80//!   "0eat": { "x": 473, "y": 247 },
81//!   "1eat": { "x": 654, "y": 396 },
82//!   "2eat": { "x": 574, "y": 573 },
83//!   "3eat": { "x": 333, "y": 556 },
84//!   "4eat": { "x": 267, "y": 370 },
85//!   "1think": { "x": 842, "y": 304 },
86//!   "4think": { "x": 72, "y": 314 },
87//!   "3think": { "x": 200, "y": 726 },
88//!   "2think": { "x": 740, "y": 699 }
89//! },
90//! "arcs": [
91//!   { "source": "chopstick0", "target": "0eat" },
92//!   { "source": "chopstick1", "target": "0eat" },
93//!   { "source": "chopstick0", "target": "4eat" },
94//!   { "source": "chopstick2", "target": "4eat" },
95//!   { "source": "chopstick1", "target": "1eat" },
96//!   { "source": "chopstick3", "target": "1eat" },
97//!   { "source": "chopstick2", "target": "3eat" },
98//!   { "source": "chopstick4", "target": "3eat" },
99//!   { "source": "chopstick4", "target": "2eat" },
100//!   { "source": "chopstick3", "target": "2eat" },
101//!   { "source": "0eat", "target": "0right" },
102//!   { "source": "0eat", "target": "0left" },
103//!   { "source": "1eat", "target": "1right" },
104//!   { "source": "1eat", "target": "1left" },
105//!   { "source": "2eat", "target": "2left" },
106//!   { "source": "2eat", "target": "2right" },
107//!   { "source": "3eat", "target": "3right" },
108//!   { "source": "3eat", "target": "3left" },
109//!   { "source": "4eat", "target": "4right" },
110//!   { "source": "4eat", "target": "4left" },
111//!   { "source": "0right", "target": "0think" },
112//!   { "source": "0left", "target": "0think" },
113//!   { "source": "1right", "target": "1think" },
114//!   { "source": "1left", "target": "1think" },
115//!   { "source": "2left", "target": "2think" },
116//!   { "source": "2right", "target": "2think" },
117//!   { "source": "4left", "target": "4think" },
118//!   { "source": "4right", "target": "4think" },
119//!   { "source": "3right", "target": "3think" },
120//!   { "source": "3left", "target": "3think" },
121//!   { "source": "4think", "target": "chopstick0" },
122//!   { "source": "4think", "target": "chopstick2" },
123//!   { "source": "0think", "target": "chopstick0" },
124//!   { "source": "0think", "target": "chopstick1" },
125//!   { "source": "1think", "target": "chopstick1" },
126//!   { "source": "1think", "target": "chopstick3" },
127//!   { "source": "2think", "target": "chopstick3" },
128//!   { "source": "2think", "target": "chopstick4" },
129//!   { "source": "3think", "target": "chopstick2" },
130//!   { "source": "3think", "target": "chopstick4" }
131//! ]
132//! }};
133//! ```
134//!
135//! ![dining_philosophers][dining_philosophers]
136//!
137//! [dining_philosophers]: data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4OTAiIGhlaWdodD0iNzQwIiB2aWV3Qm94PSIxMiA0NiA4OTAgNzQwIj4KPGRlZnM+PG1hcmtlciBpZD0ibWFya2VyQXJyb3cxIiBtYXJrZXJXaWR0aD0iMjMiIG1hcmtlckhlaWdodD0iMTMiIHJlZlg9IjMxIiByZWZZPSI2IiBvcmllbnQ9ImF1dG8iPjxyZWN0IHdpZHRoPSIyOCIgaGVpZ2h0PSIzIiBmaWxsPSJ3aGl0ZSIgc3Ryb2tlPSJ3aGl0ZSIgeD0iMyIgeT0iNSIvPjxwYXRoIGQ9Ik0yLDIgTDIsMTEgTDEwLDYgTDIsMiIvPjwvbWFya2VyPjxtYXJrZXIgaWQ9Im1hcmtlckluaGliaXQxIiBtYXJrZXJXaWR0aD0iMjMiIG1hcmtlckhlaWdodD0iMTMiIHJlZlg9IjMxIiByZWZZPSI2IiBvcmllbnQ9ImF1dG8iPjxyZWN0IHdpZHRoPSIyOCIgaGVpZ2h0PSIzIiBmaWxsPSJ3aGl0ZSIgc3Ryb2tlPSJ3aGl0ZSIgeD0iMyIgeT0iNSIvPjxjaXJjbGUgY3g9IjUiIGN5PSI2LjUiIHI9IjQiLz48L21hcmtlcj48L2RlZnM+CjxnPgo8bGluZSB4MT0iNDAzIiB5MT0iMzQwIiB4Mj0iNDczIiB5Mj0iMjQ3IiBzdHJva2U9IiMwMDAwMDAiIG1hcmtlci1lbmQ9InVybCgjbWFya2VyQXJyb3cxKSIgLz4KPHRleHQgeD0iNDM0IiB5PSIyODkiIGZvbnQtc2l6ZT0ic21hbGwiPjE8L3RleHQ+CjwvZz4KPGc+CjxsaW5lIHgxPSI1MzQiIHkxPSIzNDUiIHgyPSI0NzMiIHkyPSIyNDciIHN0cm9rZT0iIzAwMDAwMCIgbWFya2VyLWVuZD0idXJsKCNtYXJrZXJBcnJvdzEpIiAvPgo8dGV4dCB4PSI0OTkiIHk9IjI5MiIgZm9udC1zaXplPSJzbWFsbCI+MTwvdGV4dD4KPC9nPgo8Zz4KPGxpbmUgeDE9IjQwMyIgeTE9IjM0MCIgeDI9IjI2NyIgeTI9IjM3MCIgc3Ryb2tlPSIjMDAwMDAwIiBtYXJrZXItZW5kPSJ1cmwoI21hcmtlckFycm93MSkiIC8+Cjx0ZXh0IHg9IjMzMSIgeT0iMzUxIiBmb250LXNpemU9InNtYWxsIj4xPC90ZXh0Pgo8L2c+CjxnPgo8bGluZSB4MT0iMzU4IiB5MT0iNDY3IiB4Mj0iMjY3IiB5Mj0iMzcwIiBzdHJva2U9IiMwMDAwMDAiIG1hcmtlci1lbmQ9InVybCgjbWFya2VyQXJyb3cxKSIgLz4KPHRleHQgeD0iMzA4IiB5PSI0MTQiIGZvbnQtc2l6ZT0ic21hbGwiPjE8L3RleHQ+CjwvZz4KPGc+CjxsaW5lIHgxPSI1MzQiIHkxPSIzNDUiIHgyPSI2NTQiIHkyPSIzOTYiIHN0cm9rZT0iIzAwMDAwMCIgbWFya2VyLWVuZD0idXJsKCNtYXJrZXJBcnJvdzEpIiAvPgo8dGV4dCB4PSI1OTAiIHk9IjM2NiIgZm9udC1zaXplPSJzbWFsbCI+MTwvdGV4dD4KPC9nPgo8Zz4KPGxpbmUgeDE9IjU0NyIgeTE9IjQ2MSIgeDI9IjY1NCIgeTI9IjM5NiIgc3Ryb2tlPSIjMDAwMDAwIiBtYXJrZXItZW5kPSJ1cmwoI21hcmtlckFycm93MSkiIC8+Cjx0ZXh0IHg9IjU5NiIgeT0iNDI0IiBmb250LXNpemU9InNtYWxsIj4xPC90ZXh0Pgo8L2c+CjxnPgo8bGluZSB4MT0iMzU4IiB5MT0iNDY3IiB4Mj0iMzMzIiB5Mj0iNTU2IiBzdHJva2U9IiMwMDAwMDAiIG1hcmtlci1lbmQ9InVybCgjbWFya2VyQXJyb3cxKSIgLz4KPHRleHQgeD0iMzQxIiB5PSI1MDciIGZvbnQtc2l6ZT0ic21hbGwiPjE8L3RleHQ+CjwvZz4KPGc+CjxsaW5lIHgxPSI0NTEiIHkxPSI1MzYiIHgyPSIzMzMiIHkyPSI1NTYiIHN0cm9rZT0iIzAwMDAwMCIgbWFya2VyLWVuZD0idXJsKCNtYXJrZXJBcnJvdzEpIiAvPgo8dGV4dCB4PSIzODgiIHk9IjU0MiIgZm9udC1zaXplPSJzbWFsbCI+MTwvdGV4dD4KPC9nPgo8Zz4KPGxpbmUgeDE9IjQ1MSIgeTE9IjUzNiIgeDI9IjU3NCIgeTI9IjU3MyIgc3Ryb2tlPSIjMDAwMDAwIiBtYXJrZXItZW5kPSJ1cmwoI21hcmtlckFycm93MSkiIC8+Cjx0ZXh0IHg9IjUwOCIgeT0iNTUwIiBmb250LXNpemU9InNtYWxsIj4xPC90ZXh0Pgo8L2c+CjxnPgo8bGluZSB4MT0iNTQ3IiB5MT0iNDYxIiB4Mj0iNTc0IiB5Mj0iNTczIiBzdHJva2U9IiMwMDAwMDAiIG1hcmtlci1lbmQ9InVybCgjbWFya2VyQXJyb3cxKSIgLz4KPHRleHQgeD0iNTU2IiB5PSI1MTMiIGZvbnQtc2l6ZT0ic21hbGwiPjE8L3RleHQ+CjwvZz4KPGc+CjxsaW5lIHgxPSI0NzMiIHkxPSIyNDciIHgyPSI0MTUiIHkyPSIxODEiIHN0cm9rZT0iIzAwMDAwMCIgbWFya2VyLWVuZD0idXJsKCNtYXJrZXJBcnJvdzEpIiAvPgo8dGV4dCB4PSI0NDAiIHk9IjIxMCIgZm9udC1zaXplPSJzbWFsbCI+MTwvdGV4dD4KPC9nPgo8Zz4KPGxpbmUgeDE9IjQ3MyIgeTE9IjI0NyIgeDI9IjU0NSIgeTI9IjE3NyIgc3Ryb2tlPSIjMDAwMDAwIiBtYXJrZXItZW5kPSJ1cmwoI21hcmtlckFycm93MSkiIC8+Cjx0ZXh0IHg9IjUwNSIgeT0iMjA4IiBmb250LXNpemU9InNtYWxsIj4xPC90ZXh0Pgo8L2c+CjxnPgo8bGluZSB4MT0iNjU0IiB5MT0iMzk2IiB4Mj0iNzE5IiB5Mj0iMjg4IiBzdHJva2U9IiMwMDAwMDAiIG1hcmtlci1lbmQ9InVybCgjbWFya2VyQXJyb3cxKSIgLz4KPHRleHQgeD0iNjgyIiB5PSIzMzgiIGZvbnQtc2l6ZT0ic21hbGwiPjE8L3RleHQ+CjwvZz4KPGc+CjxsaW5lIHgxPSI2NTQiIHkxPSIzOTYiIHgyPSI3NjkiIHkyPSI0MDQiIHN0cm9rZT0iIzAwMDAwMCIgbWFya2VyLWVuZD0idXJsKCNtYXJrZXJBcnJvdzEpIiAvPgo8dGV4dCB4PSI3MDciIHk9IjM5NiIgZm9udC1zaXplPSJzbWFsbCI+MTwvdGV4dD4KPC9nPgo8Zz4KPGxpbmUgeDE9IjU3NCIgeTE9IjU3MyIgeDI9IjY4NiIgeTI9IjU4NCIgc3Ryb2tlPSIjMDAwMDAwIiBtYXJrZXItZW5kPSJ1cmwoI21hcmtlckFycm93MSkiIC8+Cjx0ZXh0IHg9IjYyNiIgeT0iNTc0IiBmb250LXNpemU9InNtYWxsIj4xPC90ZXh0Pgo8L2c+CjxnPgo8bGluZSB4MT0iNTc0IiB5MT0iNTczIiB4Mj0iNTk0IiB5Mj0iNjc4IiBzdHJva2U9IiMwMDAwMDAiIG1hcmtlci1lbmQ9InVybCgjbWFya2VyQXJyb3cxKSIgLz4KPHRleHQgeD0iNTgwIiB5PSI2MjEiIGZvbnQtc2l6ZT0ic21hbGwiPjE8L3RleHQ+CjwvZz4KPGc+CjxsaW5lIHgxPSIzMzMiIHkxPSI1NTYiIHgyPSIyMTYiIHkyPSI2MDgiIHN0cm9rZT0iIzAwMDAwMCIgbWFya2VyLWVuZD0idXJsKCNtYXJrZXJBcnJvdzEpIiAvPgo8dGV4dCB4PSIyNzAiIHk9IjU3OCIgZm9udC1zaXplPSJzbWFsbCI+MTwvdGV4dD4KPC9nPgo8Zz4KPGxpbmUgeDE9IjMzMyIgeTE9IjU1NiIgeDI9IjMxNSIgeTI9IjY3OSIgc3Ryb2tlPSIjMDAwMDAwIiBtYXJrZXItZW5kPSJ1cmwoI21hcmtlckFycm93MSkiIC8+Cjx0ZXh0IHg9IjMyMCIgeT0iNjEzIiBmb250LXNpemU9InNtYWxsIj4xPC90ZXh0Pgo8L2c+CjxnPgo8bGluZSB4MT0iMjY3IiB5MT0iMzcwIiB4Mj0iMTQ4IiB5Mj0iMzk3IiBzdHJva2U9IiMwMDAwMDAiIG1hcmtlci1lbmQ9InVybCgjbWFya2VyQXJyb3cxKSIgLz4KPHRleHQgeD0iMjAzIiB5PSIzNzkiIGZvbnQtc2l6ZT0ic21hbGwiPjE8L3RleHQ+CjwvZz4KPGc+CjxsaW5lIHgxPSIyNjciIHkxPSIzNzAiIHgyPSIxODMiIHkyPSIyODkiIHN0cm9rZT0iIzAwMDAwMCIgbWFya2VyLWVuZD0idXJsKCNtYXJrZXJBcnJvdzEpIiAvPgo8dGV4dCB4PSIyMjEiIHk9IjMyNSIgZm9udC1zaXplPSJzbWFsbCI+MTwvdGV4dD4KPC9nPgo8Zz4KPGxpbmUgeDE9IjQxNSIgeTE9IjE4MSIgeDI9IjQ3OCIgeTI9IjEwNiIgc3Ryb2tlPSIjMDAwMDAwIiBtYXJrZXItZW5kPSJ1cmwoI21hcmtlckFycm93MSkiIC8+Cjx0ZXh0IHg9IjQ0MiIgeT0iMTM5IiBmb250LXNpemU9InNtYWxsIj4xPC90ZXh0Pgo8L2c+CjxnPgo8bGluZSB4MT0iNTQ1IiB5MT0iMTc3IiB4Mj0iNDc4IiB5Mj0iMTA2IiBzdHJva2U9IiMwMDAwMDAiIG1hcmtlci1lbmQ9InVybCgjbWFya2VyQXJyb3cxKSIgLz4KPHRleHQgeD0iNTA3IiB5PSIxMzciIGZvbnQtc2l6ZT0ic21hbGwiPjE8L3RleHQ+CjwvZz4KPGc+CjxsaW5lIHgxPSI3MTkiIHkxPSIyODgiIHgyPSI4NDIiIHkyPSIzMDQiIHN0cm9rZT0iIzAwMDAwMCIgbWFya2VyLWVuZD0idXJsKCNtYXJrZXJBcnJvdzEpIiAvPgo8dGV4dCB4PSI3NzYiIHk9IjI5MiIgZm9udC1zaXplPSJzbWFsbCI+MTwvdGV4dD4KPC9nPgo8Zz4KPGxpbmUgeDE9Ijc2OSIgeTE9IjQwNCIgeDI9Ijg0MiIgeTI9IjMwNCIgc3Ryb2tlPSIjMDAwMDAwIiBtYXJrZXItZW5kPSJ1cmwoI21hcmtlckFycm93MSkiIC8+Cjx0ZXh0IHg9IjgwMSIgeT0iMzUwIiBmb250LXNpemU9InNtYWxsIj4xPC90ZXh0Pgo8L2c+CjxnPgo8bGluZSB4MT0iNjg2IiB5MT0iNTg0IiB4Mj0iNzQwIiB5Mj0iNjk5IiBzdHJva2U9IiMwMDAwMDAiIG1hcmtlci1lbmQ9InVybCgjbWFya2VyQXJyb3cxKSIgLz4KPHRleHQgeD0iNzA5IiB5PSI2MzciIGZvbnQtc2l6ZT0ic21hbGwiPjE8L3RleHQ+CjwvZz4KPGc+CjxsaW5lIHgxPSI1OTQiIHkxPSI2NzgiIHgyPSI3NDAiIHkyPSI2OTkiIHN0cm9rZT0iIzAwMDAwMCIgbWFya2VyLWVuZD0idXJsKCNtYXJrZXJBcnJvdzEpIiAvPgo8dGV4dCB4PSI2NjMiIHk9IjY4NCIgZm9udC1zaXplPSJzbWFsbCI+MTwvdGV4dD4KPC9nPgo8Zz4KPGxpbmUgeDE9IjE4MyIgeTE9IjI4OSIgeDI9IjcyIiB5Mj0iMzE0IiBzdHJva2U9IiMwMDAwMDAiIG1hcmtlci1lbmQ9InVybCgjbWFya2VyQXJyb3cxKSIgLz4KPHRleHQgeD0iMTIzIiB5PSIyOTciIGZvbnQtc2l6ZT0ic21hbGwiPjE8L3RleHQ+CjwvZz4KPGc+CjxsaW5lIHgxPSIxNDgiIHkxPSIzOTciIHgyPSI3MiIgeTI9IjMxNCIgc3Ryb2tlPSIjMDAwMDAwIiBtYXJrZXItZW5kPSJ1cmwoI21hcmtlckFycm93MSkiIC8+Cjx0ZXh0IHg9IjEwNiIgeT0iMzUxIiBmb250LXNpemU9InNtYWxsIj4xPC90ZXh0Pgo8L2c+CjxnPgo8bGluZSB4MT0iMjE2IiB5MT0iNjA4IiB4Mj0iMjAwIiB5Mj0iNzI2IiBzdHJva2U9IiMwMDAwMDAiIG1hcmtlci1lbmQ9InVybCgjbWFya2VyQXJyb3cxKSIgLz4KPHRleHQgeD0iMjA0IiB5PSI2NjMiIGZvbnQtc2l6ZT0ic21hbGwiPjE8L3RleHQ+CjwvZz4KPGc+CjxsaW5lIHgxPSIzMTUiIHkxPSI2NzkiIHgyPSIyMDAiIHkyPSI3MjYiIHN0cm9rZT0iIzAwMDAwMCIgbWFya2VyLWVuZD0idXJsKCNtYXJrZXJBcnJvdzEpIiAvPgo8dGV4dCB4PSIyNTMiIHk9IjY5OCIgZm9udC1zaXplPSJzbWFsbCI+MTwvdGV4dD4KPC9nPgo8Zz4KPGxpbmUgeDE9IjcyIiB5MT0iMzE0IiB4Mj0iNDAzIiB5Mj0iMzQwIiBzdHJva2U9IiMwMDAwMDAiIG1hcmtlci1lbmQ9InVybCgjbWFya2VyQXJyb3cxKSIgLz4KPHRleHQgeD0iMjMzIiB5PSIzMjMiIGZvbnQtc2l6ZT0ic21hbGwiPjE8L3RleHQ+CjwvZz4KPGc+CjxsaW5lIHgxPSI3MiIgeTE9IjMxNCIgeDI9IjM1OCIgeTI9IjQ2NyIgc3Ryb2tlPSIjMDAwMDAwIiBtYXJrZXItZW5kPSJ1cmwoI21hcmtlckFycm93MSkiIC8+Cjx0ZXh0IHg9IjIxMSIgeT0iMzg2IiBmb250LXNpemU9InNtYWxsIj4xPC90ZXh0Pgo8L2c+CjxnPgo8bGluZSB4MT0iNDc4IiB5MT0iMTA2IiB4Mj0iNDAzIiB5Mj0iMzQwIiBzdHJva2U9IiMwMDAwMDAiIG1hcmtlci1lbmQ9InVybCgjbWFya2VyQXJyb3cxKSIgLz4KPHRleHQgeD0iNDM2IiB5PSIyMTkiIGZvbnQtc2l6ZT0ic21hbGwiPjE8L3RleHQ+CjwvZz4KPGc+CjxsaW5lIHgxPSI0NzgiIHkxPSIxMDYiIHgyPSI1MzQiIHkyPSIzNDUiIHN0cm9rZT0iIzAwMDAwMCIgbWFya2VyLWVuZD0idXJsKCNtYXJrZXJBcnJvdzEpIiAvPgo8dGV4dCB4PSI1MDIiIHk9IjIyMSIgZm9udC1zaXplPSJzbWFsbCI+MTwvdGV4dD4KPC9nPgo8Zz4KPGxpbmUgeDE9Ijg0MiIgeTE9IjMwNCIgeDI9IjUzNCIgeTI9IjM0NSIgc3Ryb2tlPSIjMDAwMDAwIiBtYXJrZXItZW5kPSJ1cmwoI21hcmtlckFycm93MSkiIC8+Cjx0ZXh0IHg9IjY4NCIgeT0iMzIwIiBmb250LXNpemU9InNtYWxsIj4xPC90ZXh0Pgo8L2c+CjxnPgo8bGluZSB4MT0iODQyIiB5MT0iMzA0IiB4Mj0iNTQ3IiB5Mj0iNDYxIiBzdHJva2U9IiMwMDAwMDAiIG1hcmtlci1lbmQ9InVybCgjbWFya2VyQXJyb3cxKSIgLz4KPHRleHQgeD0iNjkwIiB5PSIzNzgiIGZvbnQtc2l6ZT0ic21hbGwiPjE8L3RleHQ+CjwvZz4KPGc+CjxsaW5lIHgxPSI3NDAiIHkxPSI2OTkiIHgyPSI1NDciIHkyPSI0NjEiIHN0cm9rZT0iIzAwMDAwMCIgbWFya2VyLWVuZD0idXJsKCNtYXJrZXJBcnJvdzEpIiAvPgo8dGV4dCB4PSI2MzkiIHk9IjU3NiIgZm9udC1zaXplPSJzbWFsbCI+MTwvdGV4dD4KPC9nPgo8Zz4KPGxpbmUgeDE9Ijc0MCIgeTE9IjY5OSIgeDI9IjQ1MSIgeTI9IjUzNiIgc3Ryb2tlPSIjMDAwMDAwIiBtYXJrZXItZW5kPSJ1cmwoI21hcmtlckFycm93MSkiIC8+Cjx0ZXh0IHg9IjU5MSIgeT0iNjEzIiBmb250LXNpemU9InNtYWxsIj4xPC90ZXh0Pgo8L2c+CjxnPgo8bGluZSB4MT0iMjAwIiB5MT0iNzI2IiB4Mj0iMzU4IiB5Mj0iNDY3IiBzdHJva2U9IiMwMDAwMDAiIG1hcmtlci1lbmQ9InVybCgjbWFya2VyQXJyb3cxKSIgLz4KPHRleHQgeD0iMjc1IiB5PSI1OTIiIGZvbnQtc2l6ZT0ic21hbGwiPjE8L3RleHQ+CjwvZz4KPGc+CjxsaW5lIHgxPSIyMDAiIHkxPSI3MjYiIHgyPSI0NTEiIHkyPSI1MzYiIHN0cm9rZT0iIzAwMDAwMCIgbWFya2VyLWVuZD0idXJsKCNtYXJrZXJBcnJvdzEpIiAvPgo8dGV4dCB4PSIzMjEiIHk9IjYyNyIgZm9udC1zaXplPSJzbWFsbCI+MTwvdGV4dD4KPC9nPgo8Zz4KPGNpcmNsZSBjeD0iNTk0IiBjeT0iNjc4IiByPSIxNiIgc3Ryb2tlV2lkdGg9IjEuNSIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjMDAwMDAwIiBvcmllbnQ9IjAiIHNoYXBlUmVuZGVyaW5nPSJhdXRvIiAvPjx0ZXh0IHg9IjU3NiIgeT0iNjU4IiBmb250LXNpemU9InNtYWxsIj4ycmlnaHQ8L3RleHQ+CjwvZz4KPGc+CjxjaXJjbGUgY3g9IjIxNiIgY3k9IjYwOCIgcj0iMTYiIHN0cm9rZVdpZHRoPSIxLjUiIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0iIzAwMDAwMCIgb3JpZW50PSIwIiBzaGFwZVJlbmRlcmluZz0iYXV0byIgLz48dGV4dCB4PSIxOTgiIHk9IjU4OCIgZm9udC1zaXplPSJzbWFsbCI+M3JpZ2h0PC90ZXh0Pgo8L2c+CjxnPgo8Y2lyY2xlIGN4PSI3NjkiIGN5PSI0MDQiIHI9IjE2IiBzdHJva2VXaWR0aD0iMS41IiBmaWxsPSIjZmZmZmZmIiBzdHJva2U9IiMwMDAwMDAiIG9yaWVudD0iMCIgc2hhcGVSZW5kZXJpbmc9ImF1dG8iIC8+PHRleHQgeD0iNzUxIiB5PSIzODQiIGZvbnQtc2l6ZT0ic21hbGwiPjFsZWZ0PC90ZXh0Pgo8L2c+CjxnPgo8Y2lyY2xlIGN4PSI1MzQiIGN5PSIzNDUiIHI9IjE2IiBzdHJva2VXaWR0aD0iMS41IiBmaWxsPSIjZmZmZmZmIiBzdHJva2U9IiMwMDAwMDAiIG9yaWVudD0iMCIgc2hhcGVSZW5kZXJpbmc9ImF1dG8iIC8+PHRleHQgeD0iNTE2IiB5PSIzMjUiIGZvbnQtc2l6ZT0ic21hbGwiPmNob3BzdGljazE8L3RleHQ+PGNpcmNsZSBjeD0iNTM0IiBjeT0iMzQ1IiByPSIyIiBmaWxsPSIjMDAwMDAwIiBzdHJva2U9IiMwMDAwMDAiIG9yaWVudD0iMCIgY2xhc3NOYW1lPSJ0b2tlbnMiIC8+CjwvZz4KPGc+CjxjaXJjbGUgY3g9IjM1OCIgY3k9IjQ2NyIgcj0iMTYiIHN0cm9rZVdpZHRoPSIxLjUiIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0iIzAwMDAwMCIgb3JpZW50PSIwIiBzaGFwZVJlbmRlcmluZz0iYXV0byIgLz48dGV4dCB4PSIzNDAiIHk9IjQ0NyIgZm9udC1zaXplPSJzbWFsbCI+Y2hvcHN0aWNrMjwvdGV4dD4KPC9nPgo8Zz4KPGNpcmNsZSBjeD0iNzE5IiBjeT0iMjg4IiByPSIxNiIgc3Ryb2tlV2lkdGg9IjEuNSIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjMDAwMDAwIiBvcmllbnQ9IjAiIHNoYXBlUmVuZGVyaW5nPSJhdXRvIiAvPjx0ZXh0IHg9IjcwMSIgeT0iMjY4IiBmb250LXNpemU9InNtYWxsIj4xcmlnaHQ8L3RleHQ+CjwvZz4KPGc+CjxjaXJjbGUgY3g9IjU0NSIgY3k9IjE3NyIgcj0iMTYiIHN0cm9rZVdpZHRoPSIxLjUiIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0iIzAwMDAwMCIgb3JpZW50PSIwIiBzaGFwZVJlbmRlcmluZz0iYXV0byIgLz48dGV4dCB4PSI1MjciIHk9IjE1NyIgZm9udC1zaXplPSJzbWFsbCI+MGxlZnQ8L3RleHQ+CjwvZz4KPGc+CjxjaXJjbGUgY3g9IjY4NiIgY3k9IjU4NCIgcj0iMTYiIHN0cm9rZVdpZHRoPSIxLjUiIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0iIzAwMDAwMCIgb3JpZW50PSIwIiBzaGFwZVJlbmRlcmluZz0iYXV0byIgLz48dGV4dCB4PSI2NjgiIHk9IjU2NCIgZm9udC1zaXplPSJzbWFsbCI+MmxlZnQ8L3RleHQ+CjwvZz4KPGc+CjxjaXJjbGUgY3g9IjU0NyIgY3k9IjQ2MSIgcj0iMTYiIHN0cm9rZVdpZHRoPSIxLjUiIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0iIzAwMDAwMCIgb3JpZW50PSIwIiBzaGFwZVJlbmRlcmluZz0iYXV0byIgLz48dGV4dCB4PSI1MjkiIHk9IjQ0MSIgZm9udC1zaXplPSJzbWFsbCI+Y2hvcHN0aWNrMzwvdGV4dD48Y2lyY2xlIGN4PSI1NDciIGN5PSI0NjEiIHI9IjIiIGZpbGw9IiMwMDAwMDAiIHN0cm9rZT0iIzAwMDAwMCIgb3JpZW50PSIwIiBjbGFzc05hbWU9InRva2VucyIgLz4KPC9nPgo8Zz4KPGNpcmNsZSBjeD0iMzE1IiBjeT0iNjc5IiByPSIxNiIgc3Ryb2tlV2lkdGg9IjEuNSIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjMDAwMDAwIiBvcmllbnQ9IjAiIHNoYXBlUmVuZGVyaW5nPSJhdXRvIiAvPjx0ZXh0IHg9IjI5NyIgeT0iNjU5IiBmb250LXNpemU9InNtYWxsIj4zbGVmdDwvdGV4dD4KPC9nPgo8Zz4KPGNpcmNsZSBjeD0iMTgzIiBjeT0iMjg5IiByPSIxNiIgc3Ryb2tlV2lkdGg9IjEuNSIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjMDAwMDAwIiBvcmllbnQ9IjAiIHNoYXBlUmVuZGVyaW5nPSJhdXRvIiAvPjx0ZXh0IHg9IjE2NSIgeT0iMjY5IiBmb250LXNpemU9InNtYWxsIj40bGVmdDwvdGV4dD48Y2lyY2xlIGN4PSIxODMiIGN5PSIyODkiIHI9IjIiIGZpbGw9IiMwMDAwMDAiIHN0cm9rZT0iIzAwMDAwMCIgb3JpZW50PSIwIiBjbGFzc05hbWU9InRva2VucyIgLz4KPC9nPgo8Zz4KPGNpcmNsZSBjeD0iMTQ4IiBjeT0iMzk3IiByPSIxNiIgc3Ryb2tlV2lkdGg9IjEuNSIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjMDAwMDAwIiBvcmllbnQ9IjAiIHNoYXBlUmVuZGVyaW5nPSJhdXRvIiAvPjx0ZXh0IHg9IjEzMCIgeT0iMzc3IiBmb250LXNpemU9InNtYWxsIj40cmlnaHQ8L3RleHQ+PGNpcmNsZSBjeD0iMTQ4IiBjeT0iMzk3IiByPSIyIiBmaWxsPSIjMDAwMDAwIiBzdHJva2U9IiMwMDAwMDAiIG9yaWVudD0iMCIgY2xhc3NOYW1lPSJ0b2tlbnMiIC8+CjwvZz4KPGc+CjxjaXJjbGUgY3g9IjQ1MSIgY3k9IjUzNiIgcj0iMTYiIHN0cm9rZVdpZHRoPSIxLjUiIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0iIzAwMDAwMCIgb3JpZW50PSIwIiBzaGFwZVJlbmRlcmluZz0iYXV0byIgLz48dGV4dCB4PSI0MzMiIHk9IjUxNiIgZm9udC1zaXplPSJzbWFsbCI+Y2hvcHN0aWNrNDwvdGV4dD48Y2lyY2xlIGN4PSI0NTEiIGN5PSI1MzYiIHI9IjIiIGZpbGw9IiMwMDAwMDAiIHN0cm9rZT0iIzAwMDAwMCIgb3JpZW50PSIwIiBjbGFzc05hbWU9InRva2VucyIgLz4KPC9nPgo8Zz4KPGNpcmNsZSBjeD0iNDE1IiBjeT0iMTgxIiByPSIxNiIgc3Ryb2tlV2lkdGg9IjEuNSIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjMDAwMDAwIiBvcmllbnQ9IjAiIHNoYXBlUmVuZGVyaW5nPSJhdXRvIiAvPjx0ZXh0IHg9IjM5NyIgeT0iMTYxIiBmb250LXNpemU9InNtYWxsIj4wcmlnaHQ8L3RleHQ+CjwvZz4KPGc+CjxjaXJjbGUgY3g9IjQwMyIgY3k9IjM0MCIgcj0iMTYiIHN0cm9rZVdpZHRoPSIxLjUiIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0iIzAwMDAwMCIgb3JpZW50PSIwIiBzaGFwZVJlbmRlcmluZz0iYXV0byIgLz48dGV4dCB4PSIzODUiIHk9IjMyMCIgZm9udC1zaXplPSJzbWFsbCI+Y2hvcHN0aWNrMDwvdGV4dD4KPC9nPgo8Zz4KPHJlY3QgeD0iMzE2IiB5PSI1MzkiIHdpZHRoPSIzMCIgaGVpZ2h0PSIzMCIgc3Ryb2tlPSIjMDAwMDAwIiBmaWxsPSIjZmZmZmZmIiByeD0iNCIgLz48dGV4dCB4PSIzMTYiIHk9IjUzMSIgZm9udC1zaXplPSJzbWFsbCI+M2VhdDwvdGV4dD4KPC9nPgo8Zz4KPHJlY3QgeD0iNDU2IiB5PSIyMzAiIHdpZHRoPSIzMCIgaGVpZ2h0PSIzMCIgc3Ryb2tlPSIjMDAwMDAwIiBmaWxsPSIjZmZmZmZmIiByeD0iNCIgLz48dGV4dCB4PSI0NTYiIHk9IjIyMiIgZm9udC1zaXplPSJzbWFsbCI+MGVhdDwvdGV4dD4KPC9nPgo8Zz4KPHJlY3QgeD0iNjM3IiB5PSIzNzkiIHdpZHRoPSIzMCIgaGVpZ2h0PSIzMCIgc3Ryb2tlPSIjMDAwMDAwIiBmaWxsPSIjNjJmYTc1IiByeD0iNCIgLz48dGV4dCB4PSI2MzciIHk9IjM3MSIgZm9udC1zaXplPSJzbWFsbCI+MWVhdDwvdGV4dD4KPC9nPgo8Zz4KPHJlY3QgeD0iODI1IiB5PSIyODciIHdpZHRoPSIzMCIgaGVpZ2h0PSIzMCIgc3Ryb2tlPSIjMDAwMDAwIiBmaWxsPSIjZmZmZmZmIiByeD0iNCIgLz48dGV4dCB4PSI4MjUiIHk9IjI3OSIgZm9udC1zaXplPSJzbWFsbCI+MXRoaW5rPC90ZXh0Pgo8L2c+CjxnPgo8cmVjdCB4PSI1NTciIHk9IjU1NiIgd2lkdGg9IjMwIiBoZWlnaHQ9IjMwIiBzdHJva2U9IiMwMDAwMDAiIGZpbGw9IiM2MmZhNzUiIHJ4PSI0IiAvPjx0ZXh0IHg9IjU1NyIgeT0iNTQ4IiBmb250LXNpemU9InNtYWxsIj4yZWF0PC90ZXh0Pgo8L2c+CjxnPgo8cmVjdCB4PSIxODMiIHk9IjcwOSIgd2lkdGg9IjMwIiBoZWlnaHQ9IjMwIiBzdHJva2U9IiMwMDAwMDAiIGZpbGw9IiNmZmZmZmYiIHJ4PSI0IiAvPjx0ZXh0IHg9IjE4MyIgeT0iNzAxIiBmb250LXNpemU9InNtYWxsIj4zdGhpbms8L3RleHQ+CjwvZz4KPGc+CjxyZWN0IHg9IjU1IiB5PSIyOTciIHdpZHRoPSIzMCIgaGVpZ2h0PSIzMCIgc3Ryb2tlPSIjMDAwMDAwIiBmaWxsPSIjNjJmYTc1IiByeD0iNCIgLz48dGV4dCB4PSI1NSIgeT0iMjg5IiBmb250LXNpemU9InNtYWxsIj40dGhpbms8L3RleHQ+CjwvZz4KPGc+CjxyZWN0IHg9IjI1MCIgeT0iMzUzIiB3aWR0aD0iMzAiIGhlaWdodD0iMzAiIHN0cm9rZT0iIzAwMDAwMCIgZmlsbD0iI2ZmZmZmZiIgcng9IjQiIC8+PHRleHQgeD0iMjUwIiB5PSIzNDUiIGZvbnQtc2l6ZT0ic21hbGwiPjRlYXQ8L3RleHQ+CjwvZz4KPGc+CjxyZWN0IHg9IjcyMyIgeT0iNjgyIiB3aWR0aD0iMzAiIGhlaWdodD0iMzAiIHN0cm9rZT0iIzAwMDAwMCIgZmlsbD0iI2ZmZmZmZiIgcng9IjQiIC8+PHRleHQgeD0iNzIzIiB5PSI2NzQiIGZvbnQtc2l6ZT0ic21hbGwiPjJ0aGluazwvdGV4dD4KPC9nPgo8Zz4KPHJlY3QgeD0iNDYxIiB5PSI4OSIgd2lkdGg9IjMwIiBoZWlnaHQ9IjMwIiBzdHJva2U9IiMwMDAwMDAiIGZpbGw9IiNmZmZmZmYiIHJ4PSI0IiAvPjx0ZXh0IHg9IjQ2MSIgeT0iODEiIGZvbnQtc2l6ZT0ic21hbGwiPjB0aGluazwvdGV4dD4KPC9nPgo8L3N2Zz4=
138//!
139
140// Rustc lints
141// <https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html>
142#![warn(
143    anonymous_parameters,
144    bare_trait_objects,
145    elided_lifetimes_in_paths,
146    missing_copy_implementations,
147    rust_2018_idioms,
148    single_use_lifetimes,
149    trivial_casts,
150    trivial_numeric_casts,
151    unsafe_code,
152    unused_extern_crates,
153    unused_import_braces,
154)]
155
156// Clippy lints
157// <https://rust-lang.github.io/rust-clippy/current/>
158#![warn(
159    clippy::all,
160    clippy::cargo,
161    clippy::dbg_macro,
162    clippy::float_cmp_const,
163    clippy::get_unwrap,
164    clippy::mem_forget,
165    clippy::nursery,
166    clippy::pedantic,
167    clippy::todo,
168    clippy::unwrap_used,
169)]
170
171// Allow some clippy lints
172#![allow(
173    clippy::cargo_common_metadata,
174    clippy::default_trait_access,
175    clippy::doc_markdown,
176    clippy::enum_glob_use,
177    clippy::enum_variant_names,
178    clippy::if_not_else,
179    clippy::missing_errors_doc,
180    clippy::must_use_candidate,
181    clippy::needless_pass_by_value,
182    clippy::struct_excessive_bools,
183    clippy::use_self,
184    clippy::multiple_crate_versions,
185    clippy::struct_field_names,
186    clippy::similar_names,
187)]
188
189/// The `petri_net` module contains the definition and implementation of the `PetriNet` struct.
190pub mod petri_net;
191
192/// The `oid` module is used to generate CID's for the zipped blobs.
193pub mod oid;
194
195/// The `compression` module contains functions for zipping/unzipping models as sharable base64 blobs.
196pub mod compression;
197
198/// The `vasm` module contains the implementation of a Vector Addition State Machine (VASM).
199pub mod vasm;
200
201/// The `dsl` module contains `FlowDsl` and `Builder` traits for defining Petri-nets.
202pub mod dsl;
203
204/// The `zblob` contains utilities to facilitate loading zipped blob data as petri-nets.
205pub mod zblob;
206
207/// The `model` encapsulates the `PetriNet` and `Vasm` objects into a single `Model` object.
208pub mod model;
209
210pub use crate::model::*;
211pub use crate::vasm::*;
212
213
214/// Create a model using the pflow DSL
215/// This is the primary way to create a model for most use cases
216///
217/// ![pflow][pflow]
218///
219/// [pflow]: data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyODAiIGhlaWdodD0iMjgwIiB2aWV3Qm94PSItMzggNDIgMjgwIDI4MCI+CjxkZWZzPjxtYXJrZXIgaWQ9Im1hcmtlckFycm93MSIgbWFya2VyV2lkdGg9IjIzIiBtYXJrZXJIZWlnaHQ9IjEzIiByZWZYPSIzMSIgcmVmWT0iNiIgb3JpZW50PSJhdXRvIj48cmVjdCB3aWR0aD0iMjgiIGhlaWdodD0iMyIgZmlsbD0id2hpdGUiIHN0cm9rZT0id2hpdGUiIHg9IjMiIHk9IjUiLz48cGF0aCBkPSJNMiwyIEwyLDExIEwxMCw2IEwyLDIiLz48L21hcmtlcj48bWFya2VyIGlkPSJtYXJrZXJJbmhpYml0MSIgbWFya2VyV2lkdGg9IjIzIiBtYXJrZXJIZWlnaHQ9IjEzIiByZWZYPSIzMSIgcmVmWT0iNiIgb3JpZW50PSJhdXRvIj48cmVjdCB3aWR0aD0iMjgiIGhlaWdodD0iMyIgZmlsbD0id2hpdGUiIHN0cm9rZT0id2hpdGUiIHg9IjMiIHk9IjUiLz48Y2lyY2xlIGN4PSI1IiBjeT0iNi41IiByPSI0Ii8+PC9tYXJrZXI+PC9kZWZzPgo8Zz4KPGxpbmUgeDE9IjIyIiB5MT0iMTAyIiB4Mj0iOTkiIHkyPSIxODMiIHN0cm9rZT0iIzAwMDAwMCIgbWFya2VyLWVuZD0idXJsKCNtYXJrZXJBcnJvdzEpIiAvPgo8dGV4dCB4PSI1NiIgeT0iMTM4IiBmb250LXNpemU9InNtYWxsIj4xPC90ZXh0Pgo8L2c+CjxnPgo8bGluZSB4MT0iOTkiIHkxPSIxODMiIHgyPSIxODIiIHkyPSIxMDIiIHN0cm9rZT0iIzAwMDAwMCIgbWFya2VyLWVuZD0idXJsKCNtYXJrZXJBcnJvdzEpIiAvPgo8dGV4dCB4PSIxMzYiIHk9IjEzOCIgZm9udC1zaXplPSJzbWFsbCI+MzwvdGV4dD4KPC9nPgo8Zz4KPGxpbmUgeDE9IjIyIiB5MT0iMjYyIiB4Mj0iOTkiIHkyPSIxODMiIHN0cm9rZT0iIzAwMDAwMCIgbWFya2VyLWVuZD0idXJsKCNtYXJrZXJJbmhpYml0MSkiIC8+Cjx0ZXh0IHg9IjU2IiB5PSIyMTgiIGZvbnQtc2l6ZT0ic21hbGwiPjM8L3RleHQ+CjwvZz4KPGc+CjxsaW5lIHgxPSI5OSIgeTE9IjE4MyIgeDI9IjE4MiIgeTI9IjI2MiIgc3Ryb2tlPSIjMDAwMDAwIiBtYXJrZXItZW5kPSJ1cmwoI21hcmtlckluaGliaXQxKSIgLz4KPHRleHQgeD0iMTM2IiB5PSIyMTgiIGZvbnQtc2l6ZT0ic21hbGwiPjE8L3RleHQ+CjwvZz4KPGc+CjxjaXJjbGUgY3g9Ijk5IiBjeT0iMTgzIiByPSIxNiIgc3Ryb2tlV2lkdGg9IjEuNSIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjMDAwMDAwIiBvcmllbnQ9IjAiIHNoYXBlUmVuZGVyaW5nPSJhdXRvIiAvPjx0ZXh0IHg9IjgxIiB5PSIxNjMiIGZvbnQtc2l6ZT0ic21hbGwiPnBsYWNlMDwvdGV4dD4KPC9nPgo8Zz4KPHJlY3QgeD0iMTY1IiB5PSI4NSIgd2lkdGg9IjMwIiBoZWlnaHQ9IjMwIiBzdHJva2U9IiMwMDAwMDAiIGZpbGw9IiNmZmZmZmYiIHJ4PSI0IiAvPjx0ZXh0IHg9IjE2NSIgeT0iNzciIGZvbnQtc2l6ZT0ic21hbGwiPnR4bjE8L3RleHQ+CjwvZz4KPGc+CjxyZWN0IHg9IjUiIHk9IjI0NSIgd2lkdGg9IjMwIiBoZWlnaHQ9IjMwIiBzdHJva2U9IiMwMDAwMDAiIGZpbGw9IiNmYWI1YjAiIHJ4PSI0IiAvPjx0ZXh0IHg9IjUiIHk9IjIzNyIgZm9udC1zaXplPSJzbWFsbCI+dHhuMjwvdGV4dD4KPC9nPgo8Zz4KPHJlY3QgeD0iMTY1IiB5PSIyNDUiIHdpZHRoPSIzMCIgaGVpZ2h0PSIzMCIgc3Ryb2tlPSIjMDAwMDAwIiBmaWxsPSIjNjJmYTc1IiByeD0iNCIgLz48dGV4dCB4PSIxNjUiIHk9IjIzNyIgZm9udC1zaXplPSJzbWFsbCI+dHhuMzwvdGV4dD4KPC9nPgo8Zz4KPHJlY3QgeD0iNSIgeT0iODUiIHdpZHRoPSIzMCIgaGVpZ2h0PSIzMCIgc3Ryb2tlPSIjMDAwMDAwIiBmaWxsPSIjNjJmYTc1IiByeD0iNCIgLz48dGV4dCB4PSI1IiB5PSI3NyIgZm9udC1zaXplPSJzbWFsbCI+dHhuMDwvdGV4dD4KPC9nPgo8L3N2Zz4=
220/// # Example
221///
222/// ```
223/// use pflow_metamodel::*;
224///
225/// let model: Model = pflow_dsl!{
226///    declare "petriNet"
227///    cell "place0", 0, 3, [100, 180]
228///    func "txn0", "default", [20, 100]
229///    func "txn1", "default", [180, 100]
230///    func "txn2", "default", [20, 260]
231///    func "txn3", "default", [180, 260]
232///    arrow "txn0", "place0", 1
233///    arrow "place0", "txn1", 3
234///    guard "txn2", "place0", 3
235///    guard "place0", "txn3", 1
236/// };
237///
238/// let state = model.vm.initial_vector();
239/// assert_eq!(state, vec![0]);
240/// let res = model.vm.transform(&state, "txn0", 1);
241/// assert!(res.ok);
242/// assert_eq!(state, vec![0]); // input state is _not_ mutated
243/// assert_eq!(res.output, vec![1]);
244/// let t = model.net.transitions.get("txn0");
245/// assert!(t.is_some());
246/// ```
247#[macro_export]
248macro_rules! pflow_dsl {
249    ($($name:ident $($args:expr),*)*) => {{
250        declaration_function! {
251            |p: &mut dyn dsl::Dsl| {
252                #[allow(unused)]
253                fn declare(p: &mut dyn dsl::Dsl, model_type: &str) {
254                    p.model_type(model_type);
255                }
256                #[allow(unused)]
257                fn cell(p: &mut dyn dsl::Dsl, name: &str, initial: i32, capacity: i32, coords: [i32; 2]) {
258                    p.cell(name, Option::from(initial), Option::from(capacity), coords[0],coords[1]);
259                }
260                #[allow(unused)]
261                fn func(p: &mut dyn dsl::Dsl, name: &str, role: &str, coords: [i32; 2]) {
262                    p.func(name, role, coords[0], coords[1]);
263                }
264                #[allow(unused)]
265                fn arrow(p: &mut dyn dsl::Dsl, source: &str, target: &str, weight: i32) {
266                    p.arrow(source, target, weight);
267                }
268                #[allow(unused)]
269                fn guard(p: &mut dyn dsl::Dsl, source: &str, target: &str, weight: i32) {
270                    p.guard(source, target, weight);
271                }
272                $(
273                    $name(p, $($args),*);
274                )*
275            }
276        }
277    }};
278}
279
280/// Create a model using the internal DSL functions without macro rewriting
281/// Generally not used directly, but may be useful for deeper integration with other libraries
282///
283/// ![pflow][pflow]
284///
285/// [pflow]: data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyODAiIGhlaWdodD0iMjgwIiB2aWV3Qm94PSItMzggNDIgMjgwIDI4MCI+CjxkZWZzPjxtYXJrZXIgaWQ9Im1hcmtlckFycm93MSIgbWFya2VyV2lkdGg9IjIzIiBtYXJrZXJIZWlnaHQ9IjEzIiByZWZYPSIzMSIgcmVmWT0iNiIgb3JpZW50PSJhdXRvIj48cmVjdCB3aWR0aD0iMjgiIGhlaWdodD0iMyIgZmlsbD0id2hpdGUiIHN0cm9rZT0id2hpdGUiIHg9IjMiIHk9IjUiLz48cGF0aCBkPSJNMiwyIEwyLDExIEwxMCw2IEwyLDIiLz48L21hcmtlcj48bWFya2VyIGlkPSJtYXJrZXJJbmhpYml0MSIgbWFya2VyV2lkdGg9IjIzIiBtYXJrZXJIZWlnaHQ9IjEzIiByZWZYPSIzMSIgcmVmWT0iNiIgb3JpZW50PSJhdXRvIj48cmVjdCB3aWR0aD0iMjgiIGhlaWdodD0iMyIgZmlsbD0id2hpdGUiIHN0cm9rZT0id2hpdGUiIHg9IjMiIHk9IjUiLz48Y2lyY2xlIGN4PSI1IiBjeT0iNi41IiByPSI0Ii8+PC9tYXJrZXI+PC9kZWZzPgo8Zz4KPGxpbmUgeDE9IjIyIiB5MT0iMTAyIiB4Mj0iOTkiIHkyPSIxODMiIHN0cm9rZT0iIzAwMDAwMCIgbWFya2VyLWVuZD0idXJsKCNtYXJrZXJBcnJvdzEpIiAvPgo8dGV4dCB4PSI1NiIgeT0iMTM4IiBmb250LXNpemU9InNtYWxsIj4xPC90ZXh0Pgo8L2c+CjxnPgo8bGluZSB4MT0iOTkiIHkxPSIxODMiIHgyPSIxODIiIHkyPSIxMDIiIHN0cm9rZT0iIzAwMDAwMCIgbWFya2VyLWVuZD0idXJsKCNtYXJrZXJBcnJvdzEpIiAvPgo8dGV4dCB4PSIxMzYiIHk9IjEzOCIgZm9udC1zaXplPSJzbWFsbCI+MzwvdGV4dD4KPC9nPgo8Zz4KPGxpbmUgeDE9IjIyIiB5MT0iMjYyIiB4Mj0iOTkiIHkyPSIxODMiIHN0cm9rZT0iIzAwMDAwMCIgbWFya2VyLWVuZD0idXJsKCNtYXJrZXJJbmhpYml0MSkiIC8+Cjx0ZXh0IHg9IjU2IiB5PSIyMTgiIGZvbnQtc2l6ZT0ic21hbGwiPjM8L3RleHQ+CjwvZz4KPGc+CjxsaW5lIHgxPSI5OSIgeTE9IjE4MyIgeDI9IjE4MiIgeTI9IjI2MiIgc3Ryb2tlPSIjMDAwMDAwIiBtYXJrZXItZW5kPSJ1cmwoI21hcmtlckluaGliaXQxKSIgLz4KPHRleHQgeD0iMTM2IiB5PSIyMTgiIGZvbnQtc2l6ZT0ic21hbGwiPjE8L3RleHQ+CjwvZz4KPGc+CjxjaXJjbGUgY3g9Ijk5IiBjeT0iMTgzIiByPSIxNiIgc3Ryb2tlV2lkdGg9IjEuNSIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjMDAwMDAwIiBvcmllbnQ9IjAiIHNoYXBlUmVuZGVyaW5nPSJhdXRvIiAvPjx0ZXh0IHg9IjgxIiB5PSIxNjMiIGZvbnQtc2l6ZT0ic21hbGwiPnBsYWNlMDwvdGV4dD4KPC9nPgo8Zz4KPHJlY3QgeD0iMTY1IiB5PSI4NSIgd2lkdGg9IjMwIiBoZWlnaHQ9IjMwIiBzdHJva2U9IiMwMDAwMDAiIGZpbGw9IiNmZmZmZmYiIHJ4PSI0IiAvPjx0ZXh0IHg9IjE2NSIgeT0iNzciIGZvbnQtc2l6ZT0ic21hbGwiPnR4bjE8L3RleHQ+CjwvZz4KPGc+CjxyZWN0IHg9IjUiIHk9IjI0NSIgd2lkdGg9IjMwIiBoZWlnaHQ9IjMwIiBzdHJva2U9IiMwMDAwMDAiIGZpbGw9IiNmYWI1YjAiIHJ4PSI0IiAvPjx0ZXh0IHg9IjUiIHk9IjIzNyIgZm9udC1zaXplPSJzbWFsbCI+dHhuMjwvdGV4dD4KPC9nPgo8Zz4KPHJlY3QgeD0iMTY1IiB5PSIyNDUiIHdpZHRoPSIzMCIgaGVpZ2h0PSIzMCIgc3Ryb2tlPSIjMDAwMDAwIiBmaWxsPSIjNjJmYTc1IiByeD0iNCIgLz48dGV4dCB4PSIxNjUiIHk9IjIzNyIgZm9udC1zaXplPSJzbWFsbCI+dHhuMzwvdGV4dD4KPC9nPgo8Zz4KPHJlY3QgeD0iNSIgeT0iODUiIHdpZHRoPSIzMCIgaGVpZ2h0PSIzMCIgc3Ryb2tlPSIjMDAwMDAwIiBmaWxsPSIjNjJmYTc1IiByeD0iNCIgLz48dGV4dCB4PSI1IiB5PSI3NyIgZm9udC1zaXplPSJzbWFsbCI+dHhuMDwvdGV4dD4KPC9nPgo8L3N2Zz4=
286///
287/// # Example
288///
289/// ```
290/// use pflow_metamodel::*;
291///
292/// let model = declaration_function! {
293///    |p: &mut dyn dsl::Dsl| {
294///       p.model_type("petriNet");
295///       p.cell("place0", Option::from(0), Option::from(3), 100, 180);
296///       p.func("txn0", "default", 20, 100);
297///       p.func("txn1", "default", 180, 100);
298///       p.func("txn2", "default", 20, 260);
299///       p.func("txn3", "default", 180, 260);
300///       p.arrow("txn0", "place0", 1);
301///       p.arrow("place0", "txn1", 3);
302///       p.guard("txn2", "place0", 3);
303///       p.guard("place0", "txn3", 1);
304///     }
305/// };
306///
307/// let state = model.vm.initial_vector();
308/// assert_eq!(state, vec![0]);
309/// let res = model.vm.transform(&state, "txn0", 1);
310/// assert!(res.ok);
311/// assert_eq!(state, vec![0]); // input state is _not_ mutated
312/// assert_eq!(res.output, vec![1]);
313/// let t = model.net.transitions.get("txn0");
314/// assert!(t.is_some());
315/// ```
316#[macro_export]
317macro_rules! declaration_function {
318    ($($flow_dsl:tt)*) => {{
319        let model = model::Model::new(
320            $($flow_dsl)*
321        );
322        model
323    }};
324}
325
326/// Create a model from a JSON string compatible with pflow.xyz
327///
328/// ![pflow][pflow]
329///
330/// [pflow]: data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyODAiIGhlaWdodD0iMjgwIiB2aWV3Qm94PSItMzggNDIgMjgwIDI4MCI+CjxkZWZzPjxtYXJrZXIgaWQ9Im1hcmtlckFycm93MSIgbWFya2VyV2lkdGg9IjIzIiBtYXJrZXJIZWlnaHQ9IjEzIiByZWZYPSIzMSIgcmVmWT0iNiIgb3JpZW50PSJhdXRvIj48cmVjdCB3aWR0aD0iMjgiIGhlaWdodD0iMyIgZmlsbD0id2hpdGUiIHN0cm9rZT0id2hpdGUiIHg9IjMiIHk9IjUiLz48cGF0aCBkPSJNMiwyIEwyLDExIEwxMCw2IEwyLDIiLz48L21hcmtlcj48bWFya2VyIGlkPSJtYXJrZXJJbmhpYml0MSIgbWFya2VyV2lkdGg9IjIzIiBtYXJrZXJIZWlnaHQ9IjEzIiByZWZYPSIzMSIgcmVmWT0iNiIgb3JpZW50PSJhdXRvIj48cmVjdCB3aWR0aD0iMjgiIGhlaWdodD0iMyIgZmlsbD0id2hpdGUiIHN0cm9rZT0id2hpdGUiIHg9IjMiIHk9IjUiLz48Y2lyY2xlIGN4PSI1IiBjeT0iNi41IiByPSI0Ii8+PC9tYXJrZXI+PC9kZWZzPgo8Zz4KPGxpbmUgeDE9IjIyIiB5MT0iMTAyIiB4Mj0iOTkiIHkyPSIxODMiIHN0cm9rZT0iIzAwMDAwMCIgbWFya2VyLWVuZD0idXJsKCNtYXJrZXJBcnJvdzEpIiAvPgo8dGV4dCB4PSI1NiIgeT0iMTM4IiBmb250LXNpemU9InNtYWxsIj4xPC90ZXh0Pgo8L2c+CjxnPgo8bGluZSB4MT0iOTkiIHkxPSIxODMiIHgyPSIxODIiIHkyPSIxMDIiIHN0cm9rZT0iIzAwMDAwMCIgbWFya2VyLWVuZD0idXJsKCNtYXJrZXJBcnJvdzEpIiAvPgo8dGV4dCB4PSIxMzYiIHk9IjEzOCIgZm9udC1zaXplPSJzbWFsbCI+MzwvdGV4dD4KPC9nPgo8Zz4KPGxpbmUgeDE9IjIyIiB5MT0iMjYyIiB4Mj0iOTkiIHkyPSIxODMiIHN0cm9rZT0iIzAwMDAwMCIgbWFya2VyLWVuZD0idXJsKCNtYXJrZXJJbmhpYml0MSkiIC8+Cjx0ZXh0IHg9IjU2IiB5PSIyMTgiIGZvbnQtc2l6ZT0ic21hbGwiPjM8L3RleHQ+CjwvZz4KPGc+CjxsaW5lIHgxPSI5OSIgeTE9IjE4MyIgeDI9IjE4MiIgeTI9IjI2MiIgc3Ryb2tlPSIjMDAwMDAwIiBtYXJrZXItZW5kPSJ1cmwoI21hcmtlckluaGliaXQxKSIgLz4KPHRleHQgeD0iMTM2IiB5PSIyMTgiIGZvbnQtc2l6ZT0ic21hbGwiPjE8L3RleHQ+CjwvZz4KPGc+CjxjaXJjbGUgY3g9Ijk5IiBjeT0iMTgzIiByPSIxNiIgc3Ryb2tlV2lkdGg9IjEuNSIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjMDAwMDAwIiBvcmllbnQ9IjAiIHNoYXBlUmVuZGVyaW5nPSJhdXRvIiAvPjx0ZXh0IHg9IjgxIiB5PSIxNjMiIGZvbnQtc2l6ZT0ic21hbGwiPnBsYWNlMDwvdGV4dD4KPC9nPgo8Zz4KPHJlY3QgeD0iMTY1IiB5PSI4NSIgd2lkdGg9IjMwIiBoZWlnaHQ9IjMwIiBzdHJva2U9IiMwMDAwMDAiIGZpbGw9IiNmZmZmZmYiIHJ4PSI0IiAvPjx0ZXh0IHg9IjE2NSIgeT0iNzciIGZvbnQtc2l6ZT0ic21hbGwiPnR4bjE8L3RleHQ+CjwvZz4KPGc+CjxyZWN0IHg9IjUiIHk9IjI0NSIgd2lkdGg9IjMwIiBoZWlnaHQ9IjMwIiBzdHJva2U9IiMwMDAwMDAiIGZpbGw9IiNmYWI1YjAiIHJ4PSI0IiAvPjx0ZXh0IHg9IjUiIHk9IjIzNyIgZm9udC1zaXplPSJzbWFsbCI+dHhuMjwvdGV4dD4KPC9nPgo8Zz4KPHJlY3QgeD0iMTY1IiB5PSIyNDUiIHdpZHRoPSIzMCIgaGVpZ2h0PSIzMCIgc3Ryb2tlPSIjMDAwMDAwIiBmaWxsPSIjNjJmYTc1IiByeD0iNCIgLz48dGV4dCB4PSIxNjUiIHk9IjIzNyIgZm9udC1zaXplPSJzbWFsbCI+dHhuMzwvdGV4dD4KPC9nPgo8Zz4KPHJlY3QgeD0iNSIgeT0iODUiIHdpZHRoPSIzMCIgaGVpZ2h0PSIzMCIgc3Ryb2tlPSIjMDAwMDAwIiBmaWxsPSIjNjJmYTc1IiByeD0iNCIgLz48dGV4dCB4PSI1IiB5PSI3NyIgZm9udC1zaXplPSJzbWFsbCI+dHhuMDwvdGV4dD4KPC9nPgo8L3N2Zz4=
331///
332///
333/// # Example
334///
335/// ```
336/// use pflow_metamodel::*;
337///
338/// let model: Model = pflow_json!{{
339///    "modelType": "petriNet",
340///    "version": "v0",
341///    "places": {
342///      "place0": { "offset": 0, "capacity": 3, "x": 100, "y": 180 }
343///    },
344///    "transitions": {
345///      "txn0": { "role": "role0", "x": 20, "y": 100 },
346///      "txn1": { "role": "role0", "x": 180, "y": 100 },
347///      "txn2": { "role": "role0", "x": 20, "y": 260 },
348///      "txn3": { "role": "role0", "x": 180, "y": 260 }
349///    },
350///    "arcs": [
351///      { "source": "txn0", "target": "place0" },
352///      { "source": "place0", "target": "txn1", "weight": 3 },
353///      { "source": "txn2", "target": "place0", "weight": 3, "inhibit": true },
354///      { "source": "place0", "target": "txn3", "inhibit": true }
355///    ]
356/// }};
357///
358/// let state = model.vm.initial_vector();
359/// assert_eq!(state, vec![0]);
360/// let res = model.vm.transform(&state, "txn0", 1);
361/// assert!(res.ok);
362/// assert_eq!(state, vec![0]); // input state is _not_ mutated
363/// assert_eq!(res.output, vec![1]);
364/// let t = model.net.transitions.get("txn0");
365/// assert!(t.is_some());
366/// ```
367#[macro_export]
368macro_rules! pflow_json {
369    ($($flow_json:tt)*) => {{
370        let mut net = petri_net::PetriNet::from_json_value(
371                serde_json::json!($($flow_json)*)
372        ).expect("json fault");
373
374        let sm = vasm::StateMachine::from_model(&mut net);
375        model::Model {
376            net,
377            declaration: Vec::new(),
378            vm: Box::new(sm),
379        }
380    }};
381}
382
383/// Create a model from a diagram string
384///
385/// Example:
386///
387/// ```rust
388/// use pflow_metamodel::*;
389///
390/// // NOTICE: use uppercase for states vs lowercase for transitions
391/// let model = pflow_diagram!{ ModelType::Workflow;
392///     Water --> boil_water;
393///     boil_water --> BoiledWater;
394///     CoffeeBeans --> grind_beans;
395///     grind_beans --> GroundCoffee;
396///     BoiledWater --> brew_coffee;
397///     GroundCoffee --> brew_coffee;
398///     Filter --> brew_coffee;
399///     brew_coffee --> CoffeeInPot;
400///     CoffeeInPot --> pour_coffee;
401///     Cup --> pour_coffee;
402/// };
403/// println!("https://pflow.dev?z={}", model.net.to_zblob().base64_zipped);
404///
405/// // NOTICE: only specify states in a diagram not providing a ModelType::
406/// let state_model = pflow_diagram! {
407///     Crash --> [*];
408///     Moving --> Crash;
409///     Moving --> Still;
410///     Still --> Moving;
411///     Still --> [*];
412///     [*] --> Still;
413/// };
414///
415/// println!("https://pflow.dev?z={}", state_model.net.to_zblob().base64_zipped);
416/// ```
417#[macro_export]
418macro_rules! pflow_diagram {
419    ($($workflow_declaration:tt)*) => {
420        {
421            Model::from_diagram(stringify!($($workflow_declaration)*).to_string())
422        }
423    };
424}
425
426#[cfg(test)]
427mod tests {
428    use super::*;
429    use std::sync::{Arc, Mutex};
430
431    #[test]
432    fn test_pflow_diagram() {
433        let coffee_machine = pflow_diagram! { ModelType::Workflow;
434            Water --> boil_water;
435            boil_water --> BoiledWater;
436            CoffeeBeans --> grind_beans;
437            grind_beans --> GroundCoffee;
438            BoiledWater --> brew_coffee;
439            GroundCoffee --> brew_coffee;
440            Filter --> brew_coffee;
441            brew_coffee --> CoffeeInPot;
442            CoffeeInPot --> pour_coffee;
443            Cup --> pour_coffee;
444        };
445        let zblob = coffee_machine.net.to_zblob();
446        println!("https://pflow.dev?z={}", zblob.base64_zipped);
447    }
448
449    #[test]
450    fn test_dsl() {
451        let j = pflow_dsl! {
452            declare "petriNet"
453            cell "b", 1, 0, [1, 1]
454            cell "a", 1, 0, [1, 2]
455            func "f", "default", [2, 1]
456            func "g", "default", [2, 2]
457            arrow "a", "f", 1
458            guard "b", "g", 1
459        };
460
461        let initial = j.vm.initial_vector();
462        assert_eq!(initial, vec![1, 1]);
463
464        j.net.transitions.get("f").expect("expected transition");
465
466        let res = j.vm.transform(&initial, "f", 1);
467        assert!(res.ok);
468        assert_eq!(initial, vec![1, 1]); // input state is _not_ mutated
469        assert_eq!(res.output, vec![1, 0]);
470    }
471
472    #[test]
473    fn test_json_dsl() {
474        let j = pflow_json! {
475            {
476                "modelType": "petriNet",
477                "version": "v0",
478                "places": {
479                    "a": { "offset": 0, "initial": 1, "capacity": 1, "x": 0, "y": 0 },
480                    "b": { "offset": 1, "initial": 1, "capacity": 1, "x": 0, "y": 0 }
481                },
482                "transitions": {
483                    "f": { "role": "default", "x": 0, "y": 0 }
484                },
485                "arcs": [
486                    { "source": "a", "target": "f", "weight": 1 },
487                    { "source": "b", "target": "f", "weight": 1 }
488                ]
489            }
490        };
491
492        let initial = j.vm.initial_vector();
493        assert_eq!(initial, vec![1, 1]);
494        j.net.transitions.get("f").expect("expected transition");
495    }
496
497    #[test]
498    fn test_model() {
499        let model = declaration_function! {
500            |p: &mut dyn dsl::Dsl| {
501                p.model_type("petriNet");
502                p.cell("b", Some(1), None, 0, 0);
503                p.func("f", "default", 1, 1);
504                p.cell("a", Some(1), None, 0, 0);
505                p.func("g", "default", 1, 1);
506                p.arrow("a", "f", 1);
507                p.guard("b", "g", 1);
508            }
509        };
510
511        assert_eq!(model.net.model_type, "petriNet");
512        let zblob = model.net.to_zblob();
513        assert_eq!(
514            zblob.ipfs_cid,
515            "zb2rhXMTtKZq96QpdSzkSYmEPKttirMw4okCG8c5QxwygAvWG"
516        );
517    }
518
519    #[test]
520    fn test_workflow_model() {
521        let model = pflow_dsl! {
522            declare "workflow"
523            cell "Water", 0, 1, [100, 300]
524            cell "CoffeeBeans", 0, 1, [180, 300]
525            cell "BoiledWater", 0, 1, [195, 397]
526            cell "GroundCoffee", 0, 1, [250, 339]
527            cell "Filter", 0, 1, [290, 280]
528            cell "CoffeeInPot", 0, 1, [328, 366]
529            cell "Cup", 0, 1, [365, 312]
530            cell "step0", 1, 1, [100, 100]
531            cell "step1", 0, 1, [180, 100]
532            cell "step2", 0, 1, [260, 100]
533            cell "step3", 0, 1, [340, 100]
534            cell "step4", 0, 1, [420, 100]
535            func "boil_water", "coffee_machine", [100, 200]
536            func "brew_coffee", "coffee_machine", [260, 200]
537            func "grind_beans", "coffee_machine", [180, 200]
538            func "pour_coffee", "coffee_machine", [340, 200]
539            arrow "Water", "boil_water", 1
540            arrow "CoffeeBeans", "grind_beans", 1
541            arrow "BoiledWater", "brew_coffee", 1
542            arrow "GroundCoffee", "brew_coffee", 1
543            arrow "Filter", "brew_coffee", 1
544            arrow "CoffeeInPot", "pour_coffee", 1
545            arrow "Cup", "pour_coffee", 1
546            arrow "step0", "boil_water", 1
547            arrow "boil_water", "step1", 1
548            arrow "step1", "grind_beans", 1
549            arrow "grind_beans", "step2", 1
550            arrow "step2", "brew_coffee", 1
551            arrow "brew_coffee", "step3", 1
552            arrow "step3", "pour_coffee", 1
553            arrow "pour_coffee", "step4", 1
554        };
555
556        let zb = model.net.to_zblob();
557        println!("https://pflow.dev?z={}", zb.base64_zipped);
558        assert_eq!(
559            zb.ipfs_cid,
560            "zb2rhcgvzu3CJ7KaRmySuR253VD2DFPqyQftHhDMKAPaQRzjE"
561        );
562
563        let state = Arc::new(Mutex::new(model.vm.initial_vector()));
564        {
565            let mut state_lock = state.lock().expect("state lock");
566            let res = model.vm.transform(&state_lock, "boil_water", 1);
567            if res.ok {
568                println!("{res:?}");
569                *state_lock = res.output;
570                drop(state_lock);
571            } else {
572                panic!("expected ok");
573            }
574        }
575    }
576}