1use clock::ReplicaId;
2use std::path::{Path, PathBuf};
3use tempdir::TempDir;
4
5#[derive(Clone)]
6struct Envelope<T: Clone> {
7 message: T,
8 sender: ReplicaId,
9}
10
11pub struct Network<T: Clone, R: rand::Rng> {
12 inboxes: std::collections::BTreeMap<ReplicaId, Vec<Envelope<T>>>,
13 all_messages: Vec<T>,
14 rng: R,
15}
16
17impl<T: Clone, R: rand::Rng> Network<T, R> {
18 pub fn new(rng: R) -> Self {
19 Network {
20 inboxes: Default::default(),
21 all_messages: Vec::new(),
22 rng,
23 }
24 }
25
26 pub fn add_peer(&mut self, id: ReplicaId) {
27 self.inboxes.insert(id, Vec::new());
28 }
29
30 pub fn replicate(&mut self, old_replica_id: ReplicaId, new_replica_id: ReplicaId) {
31 self.inboxes
32 .insert(new_replica_id, self.inboxes[&old_replica_id].clone());
33 }
34
35 pub fn is_idle(&self) -> bool {
36 self.inboxes.values().all(|i| i.is_empty())
37 }
38
39 pub fn broadcast(&mut self, sender: ReplicaId, messages: Vec<T>) {
40 for (replica, inbox) in self.inboxes.iter_mut() {
41 if *replica != sender {
42 for message in &messages {
43 let min_index = inbox
44 .iter()
45 .enumerate()
46 .rev()
47 .find_map(|(index, envelope)| {
48 if sender == envelope.sender {
49 Some(index + 1)
50 } else {
51 None
52 }
53 })
54 .unwrap_or(0);
55
56 // Insert one or more duplicates of this message *after* the previous
57 // message delivered by this replica.
58 for _ in 0..self.rng.gen_range(1..4) {
59 let insertion_index = self.rng.gen_range(min_index..inbox.len() + 1);
60 inbox.insert(
61 insertion_index,
62 Envelope {
63 message: message.clone(),
64 sender,
65 },
66 );
67 }
68 }
69 }
70 }
71 self.all_messages.extend(messages);
72 }
73
74 pub fn has_unreceived(&self, receiver: ReplicaId) -> bool {
75 !self.inboxes[&receiver].is_empty()
76 }
77
78 pub fn receive(&mut self, receiver: ReplicaId) -> Vec<T> {
79 let inbox = self.inboxes.get_mut(&receiver).unwrap();
80 let count = self.rng.gen_range(0..inbox.len() + 1);
81 inbox
82 .drain(0..count)
83 .map(|envelope| envelope.message)
84 .collect()
85 }
86}
87
88pub fn temp_tree(tree: serde_json::Value) -> TempDir {
89 let dir = TempDir::new("").unwrap();
90 write_tree(dir.path(), tree);
91 dir
92}
93
94fn write_tree(path: &Path, tree: serde_json::Value) {
95 use serde_json::Value;
96 use std::fs;
97
98 if let Value::Object(map) = tree {
99 for (name, contents) in map {
100 let mut path = PathBuf::from(path);
101 path.push(name);
102 match contents {
103 Value::Object(_) => {
104 fs::create_dir(&path).unwrap();
105 write_tree(&path, contents);
106 }
107 Value::Null => {
108 fs::create_dir(&path).unwrap();
109 }
110 Value::String(contents) => {
111 fs::write(&path, contents).unwrap();
112 }
113 _ => {
114 panic!("JSON object must contain only objects, strings, or null");
115 }
116 }
117 }
118 } else {
119 panic!("You must pass a JSON object to this helper")
120 }
121}
122
123pub fn sample_text(rows: usize, cols: usize, start_char: char) -> String {
124 let mut text = String::new();
125 for row in 0..rows {
126 let c: char = (start_char as u32 + row as u32) as u8 as char;
127 let mut line = c.to_string().repeat(cols);
128 if row < rows - 1 {
129 line.push('\n');
130 }
131 text += &line;
132 }
133 text
134}