use super::super::BlockKind;
use crate::interactive::tui::wrap;
pub(crate) fn label(kind: BlockKind) -> Option<&'static str> {
match kind {
BlockKind::User => Some("YOU"),
BlockKind::Assistant => Some("SAYA"),
BlockKind::Tool => Some("ACTIVITY"),
BlockKind::Table => Some("RESULT"),
BlockKind::Error => None,
BlockKind::System => None,
BlockKind::Thinking => None,
}
}
#[derive(Debug, Clone)]
pub(crate) struct Row {
pub(crate) kind: BlockKind,
pub(crate) text: String,
pub(crate) is_label: bool,
}
impl Row {
pub(crate) fn body(kind: BlockKind, text: String) -> Self {
Self {
kind,
text,
is_label: false,
}
}
pub(crate) fn label(kind: BlockKind) -> Option<Self> {
label(kind).map(|text| Self {
kind,
text: text.to_string(),
is_label: true,
})
}
}
pub(crate) type WrappedLines = Vec<Row>;
pub(crate) fn wrap_word_aware(raw: &str, width: usize, kind: BlockKind, out: &mut WrappedLines) {
out.extend(
wrap::wrap_cells(raw, width)
.into_iter()
.map(|text| Row::body(kind, text)),
);
}
#[cfg(test)]
mod wrap_tests {
use super::*;
fn wrapped_lines(input: &str, width: usize) -> Vec<String> {
let mut out = Vec::new();
wrap_word_aware(input, width, BlockKind::System, &mut out);
out.into_iter().map(|row| row.text).collect()
}
#[test]
fn wraps_on_word_boundaries_when_possible() {
assert_eq!(
wrapped_lines("alpha beta gamma", 8),
vec!["alpha", "beta", "gamma"]
);
}
#[test]
fn splits_unbreakable_tokens_but_keeps_the_rest_whole() {
let lines = wrapped_lines("abcdefghij klmno", 6);
assert_eq!(lines, vec!["abcdef", "ghij", "klmno"]);
}
#[test]
fn short_lines_pass_through_and_leading_space_never_loops() {
assert_eq!(wrapped_lines("short", 80), vec!["short"]);
assert_eq!(wrapped_lines("aaaaaaa bbb", 4), vec!["aaaa", "aaa", "bbb"]);
}
}
#[cfg(test)]
mod label_tests {
use super::*;
#[test]
fn assistant_maps_to_saya_while_your_choice_maps_to_nothing() {
assert_eq!(label(BlockKind::Assistant), Some("SAYA"));
for kind in [
BlockKind::User,
BlockKind::Assistant,
BlockKind::Tool,
BlockKind::Table,
BlockKind::Error,
BlockKind::System,
BlockKind::Thinking,
] {
let text = label(kind).unwrap_or("");
assert_ne!(text, "YOUR CHOICE", "{kind:?} must not map to YOUR CHOICE");
assert_ne!(text, "PARTIAL", "{kind:?} must not map to PARTIAL");
assert_ne!(text, "WORKING", "{kind:?} must not map to WORKING");
}
}
#[test]
fn every_kind_is_mapped_explicitly() {
assert_eq!(label(BlockKind::User), Some("YOU"));
assert_eq!(label(BlockKind::Assistant), Some("SAYA"));
assert_eq!(label(BlockKind::Tool), Some("ACTIVITY"));
assert_eq!(label(BlockKind::Table), Some("RESULT"));
assert_eq!(label(BlockKind::Error), None);
assert_eq!(label(BlockKind::System), None);
assert_eq!(label(BlockKind::Thinking), None);
}
#[test]
fn a_finished_tool_call_is_not_labelled_as_running_work() {
assert_eq!(label(BlockKind::Tool), Some("ACTIVITY"));
assert_ne!(label(BlockKind::Tool), Some("WORKING"));
}
}