pub mod diff;
pub fn truncate_str(s: &str, max_bytes: usize) -> &str {
if s.len() <= max_bytes {
return s;
}
let mut end = max_bytes;
while !s.is_char_boundary(end) {
end -= 1;
}
&s[..end]
}
pub fn tail_str(s: &str, max_bytes: usize) -> &str {
if s.len() <= max_bytes {
return s;
}
let mut start = s.len() - max_bytes;
while !s.is_char_boundary(start) {
start += 1;
}
&s[start..]
}
pub fn sanitize_terminal_text(text: &str) -> String {
text.chars()
.filter(|character| matches!(character, '\n' | '\t') || !character.is_control())
.collect()
}
#[derive(Default)]
pub struct CtrlCArm {
armed_at: Option<std::time::Instant>,
}
impl CtrlCArm {
const WINDOW: std::time::Duration = std::time::Duration::from_secs(2);
pub fn press(&mut self) -> bool {
if self.armed_at.is_some_and(|t| t.elapsed() < Self::WINDOW) {
true
} else {
self.armed_at = Some(std::time::Instant::now());
false
}
}
pub fn is_armed(&self) -> bool {
self.armed_at.is_some_and(|t| t.elapsed() < Self::WINDOW)
}
pub fn disarm(&mut self) {
self.armed_at = None;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn truncate_ascii() {
assert_eq!(truncate_str("hello", 3), "hel");
assert_eq!(truncate_str("hello", 5), "hello");
assert_eq!(truncate_str("hello", 10), "hello");
}
#[test]
fn truncate_multibyte_boundary() {
assert_eq!(truncate_str("é", 1), "");
assert_eq!(truncate_str("aéb", 2), "a");
assert_eq!(truncate_str("🦀🦀", 5), "🦀");
assert_eq!(truncate_str("🦀🦀", 4), "🦀");
assert_eq!(truncate_str("🦀🦀", 3), "");
}
#[test]
fn tail_ascii() {
assert_eq!(tail_str("hello", 3), "llo");
assert_eq!(tail_str("hello", 5), "hello");
assert_eq!(tail_str("hello", 10), "hello");
}
#[test]
fn tail_multibyte_boundary() {
assert_eq!(tail_str("aéb", 2), "b");
assert_eq!(tail_str("🦀🦀", 5), "🦀");
assert_eq!(tail_str("🦀🦀", 3), "");
}
#[test]
fn empty_string() {
assert_eq!(truncate_str("", 5), "");
assert_eq!(tail_str("", 5), "");
}
#[test]
fn terminal_text_sanitizer_strips_escape_sequences_and_bell() {
assert_eq!(
sanitize_terminal_text("safe\x1b]52;c;secret\x07\nnext\rline"),
"safe]52;c;secret\nnextline"
);
}
#[test]
fn ctrl_c_first_press_arms_second_confirms() {
let mut arm = CtrlCArm::default();
assert!(!arm.press(), "first press must not exit");
assert!(arm.is_armed());
assert!(arm.press(), "second press within window must exit");
}
#[test]
fn ctrl_c_disarm_resets() {
let mut arm = CtrlCArm::default();
assert!(!arm.press());
arm.disarm();
assert!(!arm.is_armed());
assert!(!arm.press(), "press after disarm must re-arm, not exit");
}
}