use super::*;
mod keys;
mod match_box;
impl App {
pub(super) fn ask_for_bleats(&mut self) -> Effect {
if let Some(sheep @ RowKey::Sheep(_)) = self.selected() {
self.body = Body::Bleats(BleatsPane::new(sheep));
}
Effect::None
}
#[must_use]
pub fn feed_row(&self) -> Option<&Row> {
match self.bleats_pane() {
Some(pane) => match pane.sheep() {
RowKey::Sheep(id) => self.flock.get(id),
_ => None,
},
None => match self.sheep_pane() {
Some(pane) => match pane.feed_sheep() {
RowKey::Sheep(id) => self.flock.get(id),
_ => None,
},
None => self.selected_row(),
},
}
}
#[must_use]
pub fn feed_classifier(&self) -> Classifier {
Classifier::new(self.feed_row().map_or(&[], |row| &row.info.level_rules))
}
#[must_use]
pub fn feed(&self) -> &crate::lookout::tail::Tail {
&self.feed
}
#[must_use]
pub fn bleats_pane(&self) -> Option<&BleatsPane> {
self.body.bleats_pane()
}
pub(super) fn bleats_pane_mut(&mut self) -> Option<&mut BleatsPane> {
self.body.bleats_pane_mut()
}
#[cfg(test)]
pub(crate) fn bleats_pane_mut_for_tests(&mut self) -> Option<&mut BleatsPane> {
self.bleats_pane_mut()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::lookout::view::fixtures;
#[test]
fn the_feed_follows_the_pinned_sheep_when_the_selection_moves() {
let mut app = fixtures::app_with(
vec![
ProcessInfo::builder(9, "web", ProcStatus::Online).build(),
ProcessInfo::builder(4, "billing", ProcStatus::Online).build(),
],
fixtures::plain(),
);
app.select(RowKey::Sheep(9));
let _ = app.update(Msg::Key(KeyPress::Bleats));
assert_eq!(
app.feed_row().map(|row| row.info.id),
Some(9),
"the pane opened on 9"
);
let _ = app.update(Msg::Snapshot {
rows: vec![ProcessInfo::builder(4, "billing", ProcStatus::Online).build()],
at: Instant::now(),
});
assert_eq!(
app.selected(),
Some(RowKey::Sheep(4)),
"the selection did move, which is the setup for the bug"
);
assert_eq!(
app.feed_row().map(|row| row.info.id),
None,
"and the feed reads nothing rather than billing's log"
);
}
#[test]
fn the_feed_row_follows_the_sheep_panes_own_pinned_sheep_when_the_selection_moves() {
let mut app = fixtures::app_with(
vec![
ProcessInfo::builder(9, "web", ProcStatus::Online).build(),
ProcessInfo::builder(4, "billing", ProcStatus::Online).build(),
],
fixtures::plain(),
);
app.select(RowKey::Sheep(9));
let _ = app.update(Msg::Key(KeyPress::Confirm));
assert_eq!(
app.feed_row().map(|row| row.info.id),
Some(9),
"the pane opened on 9"
);
let _ = app.update(Msg::Snapshot {
rows: vec![ProcessInfo::builder(4, "billing", ProcStatus::Online).build()],
at: Instant::now(),
});
assert_eq!(
app.selected(),
Some(RowKey::Sheep(4)),
"the selection did move, which is the setup for the bug"
);
assert_eq!(
app.feed_row().map(|row| row.info.id),
None,
"and the feed reads nothing rather than billing's log"
);
}
#[test]
fn b_opens_the_pane_on_the_selected_sheep() {
let mut app =
fixtures::with_selection(ProcessInfo::builder(9, "web", ProcStatus::Online).build());
let _ = app.update(Msg::Key(KeyPress::Bleats));
let pane = app.bleats_pane().expect("the pane is open");
assert!(matches!(pane.sheep(), RowKey::Sheep(id) if *id == 9));
}
#[test]
fn esc_from_the_bleats_pane_lands_on_the_dashboard() {
let mut app =
fixtures::with_selection(ProcessInfo::builder(9, "web", ProcStatus::Online).build());
let _ = app.update(Msg::Key(KeyPress::Bleats));
let _ = app.update(Msg::Key(KeyPress::Escape));
assert!(matches!(app.body(), Body::FlockTable));
}
#[test]
fn a_frozen_lookout_does_not_re_read_the_pane_s_log() {
let mut app =
fixtures::with_selection(ProcessInfo::builder(9, "web", ProcStatus::Online).build());
let _ = app.update(Msg::Key(KeyPress::Bleats));
let now = Instant::now();
assert_eq!(app.update(Msg::Tick { now }), Effect::RefreshFeed);
let _ = app.update(Msg::Frozen {
at_local: "2026-09-08 09:00:00".to_string(),
why: fixtures::FROZEN_WHY.to_string(),
});
assert_eq!(
app.update(Msg::Tick { now }),
Effect::None,
"a read whose result Msg::Bleats discards is not worth doing"
);
}
#[test]
fn b_with_nothing_selected_opens_no_pane() {
let mut app = fixtures::app_with(Vec::new(), fixtures::plain());
let _ = app.update(Msg::Key(KeyPress::Bleats));
assert!(app.bleats_pane().is_none());
}
}