activity/activity.rs
1// Copyright 2019 Andrew Thomas Christensen
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the
4// MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option. This file may not be copied,
5// modified, or distributed except according to those terms.
6
7use mode::{Automaton, Family, Mode};
8
9// This meta-struct represents a group of all Modes that can be used with the same Automaton, i.e. all states in the
10// same state machine. By implementing Family, we can specify the common interface that will be exposed for all states
11// (type Base) and how the current state will be stored in the Automaton (type Mode). The important thing to note is
12// that this struct will never be instantiated. It only exists to group a set of states (Modes) together.
13//
14struct ActivityFamily;
15
16impl Family for ActivityFamily {
17 // This is the public interface that will be exposed by the Automaton for all Modes in this Family.
18 type Base = dyn Activity;
19
20 // This is the type that will be stored in the Automaton and passed into the Automaton::next() function.
21 type Mode = Box<dyn Activity>;
22}
23
24// This trait defines a common interface for all Modes in ActivityFamily.
25//
26trait Activity : Mode<Family = ActivityFamily> {
27 fn update(self : Box<Self>) -> Box<dyn Activity>;
28}
29
30// Each state in the state machine implements both Activity (the Base type) and Mode.
31//
32struct Working {
33 pub hours_worked : u32,
34}
35
36impl Mode for Working {
37 type Family = ActivityFamily;
38}
39
40impl Activity for Working {
41 // This function updates the Mode and allows it to swap another one in as current, when ready.
42 //
43 fn update(mut self : Box<Self>) -> Box<dyn Activity> {
44 println!("Work, work, work...");
45 self.hours_worked += 1;
46
47 if self.hours_worked == 4 || self.hours_worked >= 8 {
48 // To swap to another Mode, we can return a new, boxed Mode with the same signature as this one. Note that
49 // because this function consumes the input Box<Self>, we can freely move state out of this Mode and into
50 // the new one that will be swapped in.
51 println!("Time for {}!", if self.hours_worked == 4 { "lunch" } else { "dinner" });
52 Box::new(Eating { hours_worked: self.hours_worked, calories_consumed: 0 })
53 }
54 else { self } // Returning self means that this Mode should remain current.
55 }
56}
57
58struct Eating {
59 pub hours_worked : u32,
60 pub calories_consumed : u32,
61}
62
63impl Mode for Eating {
64 type Family = ActivityFamily;
65}
66
67impl Activity for Eating {
68 fn update(mut self : Box<Self>) -> Box<dyn Activity> {
69 println!("Yum!");
70 self.calories_consumed += 100;
71
72 if self.calories_consumed >= 500 {
73 if self.hours_worked >= 8 {
74 println!("Time for bed!");
75 Box::new(Sleeping { hours_rested: 0 })
76 }
77 else {
78 println!("Time to go back to work!");
79 Box::new(Working { hours_worked: self.hours_worked })
80 }
81 }
82 else { self }
83 }
84}
85
86struct Sleeping {
87 pub hours_rested : u32,
88}
89
90impl Mode for Sleeping {
91 type Family = ActivityFamily;
92}
93
94impl Activity for Sleeping {
95 fn update(mut self : Box<Self>) -> Box<dyn Activity> {
96 println!("ZzZzZzZz...");
97 self.hours_rested += 1;
98
99 if self.hours_rested >= 8 {
100 println!("Time for breakfast!");
101 Box::new(Eating { hours_worked: 0, calories_consumed: 0 })
102 }
103 else { self }
104 }
105}
106
107fn main() {
108 let mut person = ActivityFamily::automaton_with_mode(Box::new(Working { hours_worked: 0 }));
109
110 for _age in 18..100 {
111 // Update the current Mode and/or transition to another Mode, when the current Mode requests it.
112 Automaton::next(&mut person, |current_mode| current_mode.update());
113 }
114}