1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
use std::collections::HashMap;
use fsm::FsmTypes;
pub type Pred<T: FsmTypes> = Box<Fn(&T::Context) -> bool>;
pub type TransitionCheck<T: FsmTypes>
= fn(&T::Context, &T::Context, &T::Msg, &Vec<T::Output>) -> Result<(), String>;
pub struct Constraints<T: FsmTypes> {
pub preconditions: HashMap<&'static str, Vec<(Pred<T>, String)>>,
pub invariants: Vec<(Pred<T>, String)>,
pub transitions: HashMap<(&'static str, &'static str), TransitionCheck<T>>
}
impl<T: FsmTypes> Constraints<T> {
pub fn new() -> Constraints<T> {
Constraints {
preconditions: HashMap::new(),
invariants: Vec::new(),
transitions: HashMap::new()
}
}
pub fn check_preconditions(&self, state: &'static str, ctx: &T::Context) -> Result<(), String> {
Constraints::<T>::check_map(&self.preconditions, state, ctx)
}
pub fn check_invariants(&self, ctx: &T::Context) -> Result<(), String> {
Constraints::<T>::check_vec(&self.invariants, ctx)
}
pub fn check_transition(&self,
from: &'static str,
to: &'static str,
init_ctx: &T::Context,
final_ctx: &T::Context,
msg: &T::Msg,
output: &Vec<T::Output>) -> Result<(), String>
{
match self.transitions.get(&(from, to)) {
None => Ok(()),
Some(check) => {
check(init_ctx, final_ctx, msg, output)
}
}
}
fn check_map(map: &HashMap<&'static str, Vec<(Pred<T>, String)>>,
state: &'static str,
ctx: &T::Context) -> Result<(), String> {
match map.get(state) {
None => Ok(()),
Some(functions) => {
Constraints::<T>::check_vec(functions, ctx)
}
}
}
fn check_vec(vec: &Vec<(Pred<T>, String)>, ctx: &T::Context) -> Result<(), String> {
for &(ref f, ref msg) in vec {
if !f(ctx) { return Err((*msg).clone()); }
}
Ok(())
}
}
#[macro_export]
macro_rules! check {
($err:expr, $predicate:expr) => {
if $predicate {
let res: Result<(), String> = Ok(());
res
} else {
return Err(format!("Error: {} Predicate: {} File: {} Line: {}",
$err, stringify!($predicate), file!(), line!()));
}
}
}
#[macro_export]
macro_rules! precondition {
($c:ident, $s:expr, $p:expr) => {{
let f = Box::new($p);
let err = constraints::errstr("precondition", $s, stringify!($p));
let mut vec = $c.preconditions.entry($s).or_insert(Vec::new());
vec.push((f, err));
}}
}
#[macro_export]
macro_rules! invariant {
($c:ident, $p:expr) => {{
let f = Box::new($p);
let err = format!("Failed invariant: {}", stringify!($p));
$c.invariants.push((f, err));
}}
}
#[macro_export]
macro_rules! transition {
($constraints:ident, $from:expr => $to:expr, $check:expr) => {{
$constraints.transitions.insert(($from, $to), $check);
}}
}
pub fn errstr(constraint: &'static str, state: &'static str, expression: &'static str) -> String{
format!("Failed {} for state {}: {}", constraint, state, expression)
}