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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//! **One engine, every name it is held under** (bl-13b9; DESIGN §4.39).
//!
//! The channel directory is the operator's, and it may lawfully name one
//! engine twice — two entries terminating at one listener (§8.2), or a
//! workspace entry enrolled before this box became an engine itself. Each is a
//! channel and each answers; painted one section a channel, the seat showed
//! one server as two engines and counted its waiting twice. The window's unit
//! is the ENGINE, so the channels are folded by the address they dial before
//! anything is counted or painted, and every name stays beside the one row.
//!
//! **It is a query over the roster and never a second roster** — the chunks
//! stay one per channel, because a reply is stamped with the channel it came
//! down and a gesture goes down the channel that can address its wall.
use crate::reply::roster::WsRow;
use super::{Aim, Chunk, Held, Model};
/// **One engine as the window shows it**: every channel this seat holds that
/// dials one address, the lead apart from the rest so that an engine with no
/// channel cannot be written.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Engine {
/// This box's own engine where it is one of them — the channel that
/// addresses every wall by its own name — else the first held.
pub lead: Chunk,
/// Every other name it is held under, in the order held.
pub also: Vec<Chunk>,
}
impl Engine {
/// The name the engine's row goes by: its lead's.
pub(crate) fn name(&self) -> &str {
&self.lead.channel.name
}
/// Every channel it is held under, the lead first.
pub(crate) fn chunks(&self) -> impl Iterator<Item = &Chunk> {
std::iter::once(&self.lead).chain(&self.also)
}
/// Whether `channel` is one of the names this engine is held under.
pub fn holds(&self, channel: &str) -> bool {
self.chunks().any(|chunk| chunk.channel.name == channel)
}
/// **Reached if any of its channels answered**; else the first that says
/// why — it will not dial, or it refused the roster; else not heard from
/// yet.
pub fn held(&self) -> Held {
let all = || self.chunks().map(|chunk| &chunk.held);
all()
.find(|held| **held == Held::Heard)
.or_else(|| all().find(|held| **held != Held::Unheard))
.unwrap_or(&self.lead.held)
.clone()
}
/// **Each wall once**, as the first channel to answer it said it.
pub fn walls(&self) -> Vec<WsRow> {
let mut out: Vec<WsRow> = Vec::new();
for wall in self.chunks().flat_map(|chunk| &chunk.walls) {
if !out.iter().any(|held| held.workspace == wall.workspace) {
out.push(wall.clone());
}
}
out
}
/// **Where a gesture at `row` goes**: down the first of this engine's
/// channels that can address it, or nowhere.
pub fn aim(&self, row: &WsRow) -> Option<Aim> {
self.chunks().find_map(|chunk| {
chunk.channel.address(row).map(|address| Aim {
channel: chunk.channel.name.clone(),
address,
})
})
}
/// **The row's words**: the lead's header, and every other name it is
/// held under beside it.
pub fn header(&self) -> String {
let lead = crate::ui::roster::header(&self.lead.channel);
if self.also.is_empty() {
return lead;
}
let also: Vec<&str> = self
.also
.iter()
.map(|chunk| chunk.channel.name.as_str())
.collect();
format!("{lead} — {ALSO} {}", also.join(", "))
}
}
/// What precedes the other names one engine is held under.
pub const ALSO: &str = "also held as";
impl Model {
/// **The roster folded by address**, in the order its first channel was
/// held. A channel that dials nothing this seat knows stands alone.
pub fn folded(&self) -> Vec<Engine> {
let mut out: Vec<Engine> = Vec::new();
for chunk in &self.roster {
let same = |engine: &&mut Engine| {
chunk.channel.dials.is_some() && engine.lead.channel.dials == chunk.channel.dials
};
match out.iter_mut().find(same) {
// The box's own engine leads: it addresses every wall.
Some(engine) if chunk.channel.named_there.is_none() => {
let was = std::mem::replace(&mut engine.lead, chunk.clone());
engine.also.insert(0, was);
}
Some(engine) => engine.also.push(chunk.clone()),
None => out.push(Engine {
lead: chunk.clone(),
also: Vec::new(),
}),
}
}
out
}
/// **The engine `channel` is one name of**, by its lead's name.
pub fn lead_of(&self, channel: &str) -> Option<String> {
self.folded()
.into_iter()
.find(|engine| engine.holds(channel))
.map(|engine| engine.name().to_owned())
}
/// **The engine a gesture down `channel` is a use of** — its lead's name,
/// and a channel this seat no longer holds is its own.
pub fn engine_of(&self, channel: &str) -> String {
self.lead_of(channel).unwrap_or_else(|| channel.to_owned())
}
}
#[cfg(test)]
mod tests;