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}