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