use std::{
io::{Stdout, stdout},
ops::{Deref, DerefMut},
os::fd::{AsRawFd, RawFd},
path::Path,
sync::{
Arc, Mutex,
atomic::{AtomicBool, Ordering},
},
thread::{self, JoinHandle},
time::{Duration, Instant},
};
use color_eyre::eyre::Result;
use crossbeam_channel::{Receiver, Sender, unbounded};
use crossterm::{
cursor,
event::{
DisableBracketedPaste, DisableFocusChange, DisableMouseCapture, EnableBracketedPaste,
EnableFocusChange, Event as CrosstermEvent, KeyEvent, KeyEventKind, poll, read,
},
terminal::{EnterAlternateScreen, LeaveAlternateScreen},
};
use ratatui::backend::CrosstermBackend as Backend;
pub const MIN_RATE: f64 = 0.1;
pub const MAX_RATE: f64 = 1000.0;
#[derive(Clone, Debug)]
pub enum Event {
Init,
Error,
Tick,
Render,
FocusGained,
FocusLost,
Key(KeyEvent),
Paste(String),
Resize(u16, u16),
}
pub struct Tui {
pub terminal: ratatui::Terminal<Backend<TerminalWriter<Stdout>>>,
event_rx: Receiver<Event>,
running: Arc<AtomicBool>,
task: Option<JoinHandle<()>>,
tick_rate: f64,
frame_rate: f64,
}
impl Tui {
pub fn new() -> Result<Self> {
let (_, event_rx) = unbounded();
Ok(Self {
terminal: ratatui::Terminal::new(Backend::new(TerminalWriter::stdout()))?,
event_rx,
running: Arc::new(AtomicBool::new(false)),
task: None,
tick_rate: 4.0,
frame_rate: 60.0,
})
}
pub fn tick_rate(mut self, tick_rate: f64) -> Self {
self.tick_rate = tick_rate;
self
}
pub fn frame_rate(mut self, frame_rate: f64) -> Self {
self.frame_rate = frame_rate;
self
}
pub fn enter(&mut self) -> Result<()> {
crossterm::terminal::enable_raw_mode()?;
set_stdin_nonblocking()?;
redirect_stderr_to_log()?;
write_enter_sequence(&mut TerminalWriter::stdout())?;
self.start();
Ok(())
}
pub fn exit(&mut self) -> Result<()> {
self.stop();
if crossterm::terminal::is_raw_mode_enabled().unwrap_or(true) {
let flushed = self.terminal.flush();
let restored = restore();
flushed?;
restored?;
}
Ok(())
}
pub fn suspend_for_child(
&mut self,
command: &mut std::process::Command,
) -> Result<std::process::ExitStatus> {
self.exit()?;
let status = command.status();
self.enter()?;
self.force_full_repaint()?;
Ok(status?)
}
fn force_full_repaint(&mut self) -> Result<()> {
let size = self.terminal.size()?;
self.terminal
.resize(ratatui::layout::Rect::new(0, 0, size.width, size.height))?;
Ok(())
}
pub fn keep_screen_for_child(
&mut self,
command: &mut std::process::Command,
) -> Result<std::process::ExitStatus> {
command
.stdin(std::process::Stdio::null())
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null());
Ok(command.status()?)
}
pub fn next_event(&self) -> Option<Event> {
self.event_rx.recv().ok()
}
pub fn start(&mut self) {
self.stop();
let (tx, event_rx) = unbounded();
self.event_rx = event_rx;
self.running.store(true, Ordering::Relaxed);
let running = Arc::clone(&self.running);
let (tick_rate, frame_rate) = (self.tick_rate, self.frame_rate);
self.task = Some(thread::spawn(move || {
event_loop(&tx, &running, tick_rate, frame_rate);
}));
}
pub fn stop(&mut self) {
self.running.store(false, Ordering::Relaxed);
if let Some(task) = self.task.take() {
let _ = task.join();
}
}
}
impl Deref for Tui {
type Target = ratatui::Terminal<Backend<TerminalWriter<Stdout>>>;
fn deref(&self) -> &Self::Target {
&self.terminal
}
}
impl DerefMut for Tui {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.terminal
}
}
impl Drop for Tui {
fn drop(&mut self) {
let _ = self.exit();
}
}
pub fn restore() -> std::io::Result<()> {
restore_in_order(
clear_stdin_nonblocking,
|| write_restore_sequence(&mut TerminalWriter::stdout()),
crossterm::terminal::disable_raw_mode,
restore_stderr,
)
}
fn restore_in_order(
clear_nonblocking: impl FnOnce() -> std::io::Result<()>,
write_sequence: impl FnOnce() -> std::io::Result<()>,
disable_raw_mode: impl FnOnce() -> std::io::Result<()>,
restore_stderr: impl FnOnce() -> std::io::Result<()>,
) -> std::io::Result<()> {
let blocking = clear_nonblocking();
let left = write_sequence();
let raw = disable_raw_mode();
let stderr = restore_stderr();
blocking.and(left).and(raw).and(stderr)
}
fn set_stdin_nonblocking() -> std::io::Result<()> {
set_fd_nonblocking(libc::STDIN_FILENO, true)
}
fn clear_stdin_nonblocking() -> std::io::Result<()> {
set_fd_nonblocking(libc::STDIN_FILENO, false)
}
fn set_fd_nonblocking(fd: RawFd, nonblocking: bool) -> std::io::Result<()> {
let flags = unsafe { libc::fcntl(fd, libc::F_GETFL) };
if flags < 0 {
return Err(std::io::Error::last_os_error());
}
let flags = if nonblocking {
flags | libc::O_NONBLOCK
} else {
flags & !libc::O_NONBLOCK
};
if unsafe { libc::fcntl(fd, libc::F_SETFL, flags) } < 0 {
return Err(std::io::Error::last_os_error());
}
Ok(())
}
const WOULD_BLOCK_PAUSE: Duration = Duration::from_millis(1);
pub struct TerminalWriter<W> {
inner: W,
}
impl TerminalWriter<Stdout> {
fn stdout() -> Self {
Self::new(stdout())
}
}
impl<W> TerminalWriter<W> {
fn new(inner: W) -> Self {
Self { inner }
}
}
impl<W: std::io::Write> TerminalWriter<W> {
fn until_the_terminal_catches_up<T>(
&mut self,
mut attempt: impl FnMut(&mut W) -> std::io::Result<T>,
) -> std::io::Result<T> {
loop {
match attempt(&mut self.inner) {
Err(error) if error.kind() == std::io::ErrorKind::WouldBlock => {
thread::sleep(WOULD_BLOCK_PAUSE);
}
outcome => return outcome,
}
}
}
}
impl<W: std::io::Write> std::io::Write for TerminalWriter<W> {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.until_the_terminal_catches_up(|inner| inner.write(buf))
}
fn flush(&mut self) -> std::io::Result<()> {
self.until_the_terminal_catches_up(|inner| inner.flush())
}
}
static SAVED_STDERR: Mutex<Option<RawFd>> = Mutex::new(None);
fn redirect_stderr_to_log() -> std::io::Result<()> {
let mut saved = SAVED_STDERR
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
if saved.is_some() {
return Ok(());
}
*saved = Some(redirect_fd_to_file(
libc::STDERR_FILENO,
&crate::logging::log_file_path(),
)?);
Ok(())
}
fn restore_stderr() -> std::io::Result<()> {
let mut saved = SAVED_STDERR
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
let Some(original) = saved.take() else {
return Ok(());
};
restore_fd(libc::STDERR_FILENO, original)
}
fn redirect_fd_to_file(target: RawFd, path: &Path) -> std::io::Result<RawFd> {
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent)?;
}
let file = std::fs::OpenOptions::new()
.create(true)
.append(true)
.open(path)?;
let saved = unsafe { libc::dup(target) };
if saved < 0 {
return Err(std::io::Error::last_os_error());
}
if unsafe { libc::dup2(file.as_raw_fd(), target) } < 0 {
let error = std::io::Error::last_os_error();
unsafe { libc::close(saved) };
return Err(error);
}
Ok(saved)
}
fn restore_fd(target: RawFd, saved: RawFd) -> std::io::Result<()> {
let result = if unsafe { libc::dup2(saved, target) } < 0 {
Err(std::io::Error::last_os_error())
} else {
Ok(())
};
unsafe { libc::close(saved) };
result
}
fn event_loop(tx: &Sender<Event>, running: &AtomicBool, tick_rate: f64, frame_rate: f64) {
let (tick, frame) = (interval(tick_rate), interval(frame_rate));
let (mut next_tick, mut next_frame) = (Instant::now(), Instant::now());
if tx.send(Event::Init).is_err() {
return;
}
while running.load(Ordering::Relaxed) {
let now = Instant::now();
if now >= next_tick {
next_tick = now + tick;
if tx.send(Event::Tick).is_err() {
return;
}
}
if now >= next_frame {
next_frame = now + frame;
if tx.send(Event::Render).is_err() {
return;
}
}
let wait = next_tick
.min(next_frame)
.saturating_duration_since(Instant::now());
let event = match poll(wait) {
Ok(true) => match read() {
Ok(event) => translate(event),
Err(_) => Some(Event::Error),
},
Ok(false) => None,
Err(_) => Some(Event::Error),
};
if let Some(event) = event
&& tx.send(event).is_err()
{
return;
}
}
}
fn interval(rate: f64) -> Duration {
let rate = if rate.is_finite() {
rate.clamp(MIN_RATE, MAX_RATE)
} else {
MIN_RATE
};
Duration::from_secs_f64(1.0 / rate)
}
fn translate(event: CrosstermEvent) -> Option<Event> {
Some(match event {
CrosstermEvent::Key(key) if key.kind == KeyEventKind::Press => Event::Key(key),
CrosstermEvent::Resize(columns, rows) => Event::Resize(columns, rows),
CrosstermEvent::FocusGained => Event::FocusGained,
CrosstermEvent::FocusLost => Event::FocusLost,
CrosstermEvent::Paste(text) => Event::Paste(text),
_ => return None,
})
}
fn write_enter_sequence(w: &mut TerminalWriter<impl std::io::Write>) -> std::io::Result<()> {
crossterm::execute!(
w,
EnterAlternateScreen,
EnableBracketedPaste,
DisableMouseCapture,
EnableFocusChange,
cursor::Hide,
)
}
fn write_restore_sequence(w: &mut TerminalWriter<impl std::io::Write>) -> std::io::Result<()> {
crossterm::execute!(
w,
DisableFocusChange,
DisableBracketedPaste,
LeaveAlternateScreen,
cursor::Show,
)
}
#[cfg(test)]
mod tests {
use std::io::Write as _;
#[derive(Default)]
struct StallingWriter {
stalls: usize,
written: Vec<u8>,
}
impl std::io::Write for StallingWriter {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
if self.stalls > 0 {
self.stalls -= 1;
return Err(std::io::Error::new(
std::io::ErrorKind::WouldBlock,
"stalled",
));
}
self.written.extend_from_slice(buf);
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
#[test]
fn a_stalled_terminal_does_not_end_the_enter_sequence() {
let mut writer = TerminalWriter::new(StallingWriter {
stalls: 3,
written: Vec::new(),
});
write_enter_sequence(&mut writer)
.expect("write the enter sequence through a stalled terminal");
assert!(
!writer.inner.written.is_empty(),
"expected the enter sequence to reach the terminal once it caught up"
);
}
#[test]
fn an_error_that_is_not_would_block_travels_unchanged() {
struct Broken;
impl std::io::Write for Broken {
fn write(&mut self, _: &[u8]) -> std::io::Result<usize> {
Err(std::io::Error::from_raw_os_error(libc::EIO))
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
let error = TerminalWriter::new(Broken)
.write(b"nowhere to go")
.expect_err("expected the error to travel rather than be waited out");
assert_eq!(error.raw_os_error(), Some(libc::EIO));
}
#[test]
fn restore_returns_the_descriptor_before_writing_and_runs_every_step_regardless() {
let ran = &std::cell::RefCell::new(Vec::new());
let step = move |name: &'static str, outcome: std::io::Result<()>| {
move || {
ran.borrow_mut().push(name);
outcome
}
};
let result = restore_in_order(
step(
"clear_nonblocking",
Err(std::io::Error::other("the flag would not clear")),
),
step("write_sequence", Ok(())),
step("disable_raw_mode", Ok(())),
step("restore_stderr", Ok(())),
);
assert_eq!(
ran.take(),
vec![
"clear_nonblocking",
"write_sequence",
"disable_raw_mode",
"restore_stderr"
],
"expected the descriptor to be handed back before the write, and every later step \
to run despite the first one failing"
);
assert!(
result.is_err(),
"expected the first step's failure to still be reported"
);
}
#[test]
fn the_non_blocking_flag_is_visible_through_every_descriptor_sharing_one_open_file() {
let mut fds = [0 as std::os::raw::c_int; 2];
assert_eq!(
unsafe { libc::pipe(fds.as_mut_ptr()) },
0,
"create a scratch pipe"
);
let (read_fd, write_fd) = (fds[0], fds[1]);
let shared_fd = unsafe { libc::dup(read_fd) };
assert!(shared_fd >= 0, "duplicate the read end");
set_fd_nonblocking(read_fd, true).expect("set one descriptor non-blocking");
let flags = unsafe { libc::fcntl(shared_fd, libc::F_GETFL) };
assert!(flags >= 0, "read the other descriptor's flags");
assert_ne!(
flags & libc::O_NONBLOCK,
0,
"expected the flag set through one descriptor to be visible through the other"
);
unsafe {
libc::close(shared_fd);
libc::close(read_fd);
libc::close(write_fd);
}
}
use crossterm::event::{KeyCode, KeyModifiers};
use super::*;
use crate::test_support::rust_source_files;
#[test]
fn redirecting_a_raw_fd_diverts_its_writes_until_restored() {
let scratch_dir = tempfile::tempdir().expect("create scratch tempdir");
let original_path = scratch_dir.path().join("original.txt");
let redirected_path = scratch_dir.path().join("redirected.txt");
let mut scratch = std::fs::OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(&original_path)
.expect("open the scratch file this test redirects");
let target = scratch.as_raw_fd();
writeln!(scratch, "before redirect").expect("write before redirect");
let saved = redirect_fd_to_file(target, &redirected_path).expect("redirect the scratch fd");
writeln!(scratch, "during redirect").expect("write during redirect");
restore_fd(target, saved).expect("restore the scratch fd");
writeln!(scratch, "after restore").expect("write after restore");
assert_eq!(
std::fs::read_to_string(&original_path).expect("read the original file"),
"before redirect\nafter restore\n",
"the fd's own file must see everything but what was written mid-redirect"
);
assert_eq!(
std::fs::read_to_string(&redirected_path).expect("read the redirected file"),
"during redirect\n",
"the redirect target must see only what was written while it was pointed there"
);
}
#[test]
fn redirect_and_restore_stderr_are_no_ops_when_already_in_that_state() {
let mut saved = SAVED_STDERR.lock().expect("lock SAVED_STDERR");
assert!(
saved.is_none(),
"test order assumption: nothing else touched fd 2"
);
*saved = Some(99);
drop(saved);
redirect_stderr_to_log().expect("redirect_stderr_to_log is a no-op once already saved");
assert_eq!(
*SAVED_STDERR.lock().expect("lock SAVED_STDERR"),
Some(99),
"a redundant redirect must not overwrite what is already saved"
);
*SAVED_STDERR.lock().expect("lock SAVED_STDERR") = None;
restore_stderr().expect("restore_stderr with nothing saved must be a no-op");
}
#[test]
fn set_fd_nonblocking_toggles_would_block_without_leaking_into_other_flags() {
let mut fds = [0 as std::os::raw::c_int; 2];
assert_eq!(
unsafe { libc::pipe(fds.as_mut_ptr()) },
0,
"create a scratch pipe"
);
let (read_fd, write_fd) = (fds[0], fds[1]);
set_fd_nonblocking(read_fd, true).expect("set the read end non-blocking");
let mut byte = [0u8; 1];
let read = unsafe { libc::read(read_fd, byte.as_mut_ptr() as *mut libc::c_void, 1) };
assert_eq!(
read, -1,
"expected a non-blocking read of an empty pipe to fail rather than return data"
);
assert_eq!(
std::io::Error::last_os_error().kind(),
std::io::ErrorKind::WouldBlock,
"expected WouldBlock rather than parking this test on an empty pipe"
);
set_fd_nonblocking(read_fd, false).expect("clear non-blocking on the read end");
let flags = unsafe { libc::fcntl(read_fd, libc::F_GETFL) };
assert_eq!(
flags & libc::O_NONBLOCK,
0,
"expected O_NONBLOCK cleared after set_fd_nonblocking(fd, false)"
);
unsafe {
libc::close(read_fd);
libc::close(write_fd);
}
}
fn ansi_bytes(command: impl crossterm::Command) -> Vec<u8> {
let mut buf = String::new();
command.write_ansi(&mut buf).expect("encode ansi command");
buf.into_bytes()
}
#[test]
fn the_enter_sequence_claims_all_four_write_based_pieces_in_order() {
let mut out = TerminalWriter::new(Vec::new());
write_enter_sequence(&mut out).expect("write enter sequence");
let mut expected = Vec::new();
expected.extend(ansi_bytes(EnterAlternateScreen));
expected.extend(ansi_bytes(EnableBracketedPaste));
expected.extend(ansi_bytes(DisableMouseCapture));
expected.extend(ansi_bytes(EnableFocusChange));
expected.extend(ansi_bytes(cursor::Hide));
assert_eq!(out.inner, expected);
}
#[test]
fn the_restore_sequence_releases_focus_paste_and_alt_screen_then_shows_the_cursor_last() {
let mut out = TerminalWriter::new(Vec::new());
write_restore_sequence(&mut out).expect("write restore sequence");
let mut expected = Vec::new();
expected.extend(ansi_bytes(DisableFocusChange));
expected.extend(ansi_bytes(DisableBracketedPaste));
expected.extend(ansi_bytes(LeaveAlternateScreen));
expected.extend(ansi_bytes(cursor::Show));
assert_eq!(out.inner, expected);
}
fn bytes_contain(haystack: &[u8], needle: &[u8]) -> bool {
!needle.is_empty()
&& haystack
.windows(needle.len())
.any(|window| window == needle)
}
fn this_files_production_source() -> String {
let manifest_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
crate::test_support::production_source_at(&manifest_dir.join("src/tui.rs"))
}
fn spec_terminal_state_pieces(spec: &str) -> Vec<(String, bool, bool)> {
const ANCHOR: &str = "## Terminal state";
let after = spec
.split(ANCHOR)
.nth(1)
.expect("the terminal state section is present");
let cell = |raw: &str| raw.trim().trim_matches('*').trim().to_string();
after
.lines()
.skip_while(|line| !line.starts_with('|'))
.take_while(|line| line.starts_with('|'))
.filter(|line| !line.starts_with("| ---"))
.filter_map(|line| {
let cells: Vec<String> = line.split('|').map(cell).collect();
let (name, setting, released) = (&cells[1], &cells[2], &cells[3]);
if name == "state" {
return None;
}
let flag = |value: &String, on: &str, off: &str| match value.as_str() {
v if v == on => true,
v if v == off => false,
other => {
panic!("unexpected {name} value {other:?} in the terminal state table")
}
};
Some((
name.clone(),
flag(setting, "on", "off"),
flag(released, "yes", "no"),
))
})
.collect()
}
#[test]
fn the_enter_and_restore_sequences_account_for_every_piece_the_spec_names() {
let manifest_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
let spec = std::fs::read_to_string(manifest_dir.join("../../docs/spec/keybindings.md"))
.expect("read docs/spec/keybindings.md");
let pieces = spec_terminal_state_pieces(&spec);
assert_eq!(
pieces,
vec![
("Raw mode".to_string(), true, true),
("Alternate screen".to_string(), true, true),
("Bracketed paste".to_string(), true, true),
("Mouse capture".to_string(), false, false),
("Focus reporting".to_string(), true, true),
],
"a piece added, removed or reworded here must be deliberately accounted for \
below, not merely counted"
);
let mut enter = TerminalWriter::new(Vec::new());
write_enter_sequence(&mut enter).expect("write enter sequence");
let mut restore = TerminalWriter::new(Vec::new());
write_restore_sequence(&mut restore).expect("write restore sequence");
let (enter, restore) = (enter.inner, restore.inner);
let source = this_files_production_source();
for (name, setting_on, released) in pieces {
if name == "Raw mode" {
assert!(
source.contains("enable_raw_mode()") && source.contains("disable_raw_mode()"),
"expected raw mode to be claimed and released by termios calls"
);
continue;
}
let (on, off) = match name.as_str() {
"Alternate screen" => (
ansi_bytes(EnterAlternateScreen),
ansi_bytes(LeaveAlternateScreen),
),
"Bracketed paste" => (
ansi_bytes(EnableBracketedPaste),
ansi_bytes(DisableBracketedPaste),
),
"Mouse capture" => (
ansi_bytes(crossterm::event::EnableMouseCapture),
ansi_bytes(DisableMouseCapture),
),
"Focus reporting" => (
ansi_bytes(EnableFocusChange),
ansi_bytes(DisableFocusChange),
),
other => panic!("no sequence known for the spec's {other:?}"),
};
let (claim, opposite) = if setting_on { (&on, &off) } else { (&off, &on) };
assert!(
bytes_contain(&enter, claim),
"expected {name} to be claimed in the enter sequence"
);
if released {
assert!(
bytes_contain(&restore, opposite),
"expected {name} to be released in the restore sequence"
);
} else {
for unwanted in [&on, &off] {
assert!(
!bytes_contain(&restore, unwanted),
"{name} is marked not released, so it must never be written on restore"
);
}
}
}
}
#[test]
fn config_md_redirects_to_the_terminal_state_contract_rather_than_restating_it() {
let manifest_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
let config_md = std::fs::read_to_string(manifest_dir.join("../../docs/spec/config.md"))
.expect("read docs/spec/config.md");
assert!(
config_md.contains("keybindings.md#terminal-state"),
"expected config.md's Launchers section to link the terminal-state contract"
);
assert!(
!config_md.contains("must be restored"),
"expected config.md to point at the contract rather than restate it"
);
}
#[test]
fn translate_passes_through_a_press_and_filters_every_other_key_event_kind() {
let press = KeyEvent::new(KeyCode::Char('q'), KeyModifiers::NONE);
assert!(matches!(
translate(CrosstermEvent::Key(press)),
Some(Event::Key(_))
));
for kind in [KeyEventKind::Release, KeyEventKind::Repeat] {
let key = KeyEvent { kind, ..press };
assert!(
translate(CrosstermEvent::Key(key)).is_none(),
"a {kind:?} key event must not reach dispatch"
);
}
}
#[test]
fn translate_passes_a_bracketed_paste_through_as_one_whole_string() {
let pasted = "first line\nsecond line".to_string();
assert!(matches!(
translate(CrosstermEvent::Paste(pasted.clone())),
Some(Event::Paste(text)) if text == pasted
));
}
#[test]
fn no_trace_of_the_removed_signal_dependency_remains() {
let manifest_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
let cargo_toml = std::fs::read_to_string(manifest_dir.join("Cargo.toml"))
.expect("read this crate's Cargo.toml");
assert!(
!cargo_toml.contains("signal-hook"),
"expected the signal-hook dependency to be gone from this crate's Cargo.toml"
);
let needle = format!("{}_{}", "signal", "hook");
let mut offending_locations = Vec::new();
for path in rust_source_files(&manifest_dir.join("src")) {
let source = std::fs::read_to_string(&path).expect("read a crate source file");
for (number, line) in source.lines().enumerate() {
let trimmed = line.trim_start();
if trimmed.starts_with("//") {
continue;
}
if line.contains(&needle) {
offending_locations.push(format!("{}:{}", path.display(), number + 1));
}
}
}
assert!(
offending_locations.is_empty(),
"expected no `{needle}` usage anywhere in this crate's source, found: \
{offending_locations:?}"
);
}
}