use super::is_listed;
use crate::config::{IconSet, ToolOverheadConfig};
use crate::events::UsageRecord;
use crate::icons::tool_overhead_icon;
use crate::input::ContextWindow;
use crate::segments::tokens::format_tokens;
use crate::theme::Palette;
pub const NAME: &str = "tool_overhead";
pub fn is_visible(segments: &[String], config: &ToolOverheadConfig) -> bool {
config.enabled && is_listed(segments, NAME)
}
pub fn render_tool_overhead_segment(
usage: Option<UsageRecord>,
ctx: &ContextWindow,
config: &ToolOverheadConfig,
icon_set: IconSet,
palette: &Palette,
) -> Option<String> {
if !config.enabled {
return None;
}
let tool_tokens = usage?.tool_tokens;
if tool_tokens < config.min_tokens {
return None;
}
let icon = tool_overhead_icon(icon_set, config.prefix.as_deref());
let r = palette.reset;
let share = ctx
.displayed_context_limit
.and_then(|limit| percent_of(tool_tokens, limit))
.map(|pct| format!(" {}({pct}%){r}", palette.dim))
.unwrap_or_default();
Some(format!(
"{}{}{} {}{}{}{}",
palette.label,
icon,
r,
palette.tokens_normal,
format_tokens(Some(tool_tokens)),
r,
share
))
}
fn percent_of(part: u64, whole: u64) -> Option<u64> {
(whole > 0).then(|| (part * 100 + whole / 2) / whole)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::segments::names;
fn ctx_with_limit(limit: u64) -> ContextWindow {
ContextWindow {
displayed_context_limit: Some(limit),
..Default::default()
}
}
fn usage(tool_tokens: u64) -> Option<UsageRecord> {
Some(UsageRecord { tool_tokens })
}
fn render(
usage: Option<UsageRecord>,
ctx: &ContextWindow,
cfg: &ToolOverheadConfig,
) -> Option<String> {
render_tool_overhead_segment(
usage,
ctx,
cfg,
IconSet::Plain,
&Palette::for_theme("plain"),
)
}
#[test]
fn test_shows_the_tool_tokens_and_their_share_of_the_window() {
let cfg = ToolOverheadConfig::default();
assert_eq!(
render(usage(68_000), &ctx_with_limit(200_000), &cfg).as_deref(),
Some("Tools: 68k (34%)")
);
}
#[test]
fn test_quiet_mode_hides_it_below_min_tokens() {
let cfg = ToolOverheadConfig {
min_tokens: 30_000,
..Default::default()
};
let ctx = ctx_with_limit(200_000);
assert_eq!(render(usage(29_999), &ctx, &cfg), None);
assert_eq!(
render(usage(30_000), &ctx, &cfg).as_deref(),
Some("Tools: 30k (15%)")
);
}
#[test]
fn test_quiet_when_disabled() {
let cfg = ToolOverheadConfig {
enabled: false,
..Default::default()
};
assert_eq!(render(usage(68_000), &ctx_with_limit(200_000), &cfg), None);
}
#[test]
fn test_shows_a_low_count_too() {
let cfg = ToolOverheadConfig::default();
assert_eq!(
render(usage(9_000), &ctx_with_limit(200_000), &cfg).as_deref(),
Some("Tools: 9.0k (5%)")
);
}
#[test]
fn test_percent_is_a_whole_number_of_the_window() {
let cfg = ToolOverheadConfig::default();
assert_eq!(
render(usage(116_000), &ctx_with_limit(200_000), &cfg).as_deref(),
Some("Tools: 116k (58%)")
);
assert_eq!(
render(usage(68_000), &ctx_with_limit(1_000_000), &cfg).as_deref(),
Some("Tools: 68k (7%)")
);
}
#[test]
fn test_omits_the_percent_without_a_usable_limit() {
let cfg = ToolOverheadConfig::default();
assert_eq!(
render(usage(68_000), &ContextWindow::default(), &cfg).as_deref(),
Some("Tools: 68k")
);
assert_eq!(
render(usage(68_000), &ctx_with_limit(0), &cfg).as_deref(),
Some("Tools: 68k")
);
}
#[test]
fn test_quiet_without_a_usage_record() {
let cfg = ToolOverheadConfig::default();
assert_eq!(render(None, &ctx_with_limit(200_000), &cfg), None);
}
#[test]
fn test_prefix_replaces_the_label() {
let cfg = ToolOverheadConfig {
prefix: Some("MCP:".to_string()),
..Default::default()
};
assert_eq!(
render(usage(68_000), &ctx_with_limit(200_000), &cfg).as_deref(),
Some("MCP: 68k (34%)")
);
}
#[test]
fn test_emoji_icon_and_theme_colors() {
let p = Palette::for_theme("github");
let rendered = render_tool_overhead_segment(
usage(68_000),
&ctx_with_limit(200_000),
&ToolOverheadConfig::default(),
IconSet::Emoji,
&p,
)
.unwrap();
assert!(rendered.contains("🧰"));
assert!(rendered.contains(&format!("{}68k{}", p.tokens_normal, p.reset)));
assert!(rendered.contains(&format!("{}(34%){}", p.dim, p.reset)));
}
#[test]
fn test_visible_when_listed_and_enabled() {
let cfg = ToolOverheadConfig::default();
assert!(is_visible(&names(&["tokens", "tool_overhead"]), &cfg));
}
#[test]
fn test_not_visible_when_unlisted_or_disabled() {
let cfg = ToolOverheadConfig::default();
assert!(!is_visible(&names(&["tokens"]), &cfg));
let disabled = ToolOverheadConfig {
enabled: false,
..Default::default()
};
assert!(!is_visible(&names(&["tool_overhead"]), &disabled));
}
}