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