linesmith_core/segments/
cost.rs1use super::{RenderContext, RenderResult, RenderedSegment, Segment, SegmentDefaults};
5use crate::data_context::DataContext;
6use crate::theme::Role;
7
8pub struct CostSegment;
9
10const PRIORITY: u8 = 192;
13
14impl Segment for CostSegment {
15 fn render(&self, ctx: &DataContext, _rc: &RenderContext) -> RenderResult {
16 let Some(cost) = ctx.status.cost.as_ref() else {
17 crate::lsm_debug!("cost: status.cost absent; hiding");
18 return Ok(None);
19 };
20 let Some(usd) = cost.total_cost_usd else {
24 crate::lsm_debug!("cost: total_cost_usd null; hiding");
25 return Ok(None);
26 };
27 Ok(Some(
28 RenderedSegment::new(format!("${usd:.2}")).with_role(Role::Muted),
29 ))
30 }
31
32 fn defaults(&self) -> SegmentDefaults {
33 SegmentDefaults::with_priority(PRIORITY)
34 }
35}
36
37#[cfg(test)]
38mod tests {
39 use super::*;
40 use crate::input::{CostMetrics, ModelInfo, StatusContext, Tool, WorkspaceInfo};
41 use std::path::PathBuf;
42 use std::sync::Arc;
43
44 fn rc() -> RenderContext {
45 RenderContext::new(80)
46 }
47
48 fn ctx(cost: Option<CostMetrics>) -> DataContext {
49 DataContext::new(StatusContext {
50 tool: Tool::ClaudeCode,
51 model: Some(ModelInfo {
52 display_name: "X".into(),
53 }),
54 workspace: Some(WorkspaceInfo {
55 project_dir: PathBuf::from("/repo"),
56 git_worktree: None,
57 }),
58 context_window: None,
59 cost,
60 effort: None,
61 vim: None,
62 output_style: None,
63 agent_name: None,
64 version: None,
65 raw: Arc::new(serde_json::Value::Null),
66 })
67 }
68
69 fn cost_of(usd: f64) -> CostMetrics {
70 CostMetrics {
71 total_cost_usd: Some(usd),
72 total_duration_ms: Some(0),
73 total_api_duration_ms: Some(0),
74 total_lines_added: Some(0),
75 total_lines_removed: Some(0),
76 }
77 }
78
79 #[test]
80 fn renders_two_decimal_places_with_muted_role() {
81 assert_eq!(
82 CostSegment
83 .render(&ctx(Some(cost_of(1.234))), &rc())
84 .unwrap(),
85 Some(RenderedSegment::new("$1.23").with_role(Role::Muted))
86 );
87 }
88
89 #[test]
90 fn renders_zero_cost() {
91 assert_eq!(
92 CostSegment.render(&ctx(Some(cost_of(0.0))), &rc()).unwrap(),
93 Some(RenderedSegment::new("$0.00").with_role(Role::Muted))
94 );
95 }
96
97 #[test]
98 fn hidden_when_cost_absent() {
99 assert_eq!(CostSegment.render(&ctx(None), &rc()).unwrap(), None);
100 }
101
102 #[test]
103 fn defaults_use_expected_priority() {
104 assert_eq!(CostSegment.defaults().priority, PRIORITY);
105 }
106}