Files
claude-code-usage-bubble/src/usage/headers.rs
T
tiennm99 e243c589a8 refactor: fix latent bugs, invert bubble→app deps, unify color path
Whole-project review pass over the entire crate. No new features.
All function definitions preserved; layout and visibility reorganised.

Bug fixes:
- bubble: ExtractIconExW HICON pair was leaked per bubble toggle.
  Extract once into a process-wide OnceLock, reuse forever (bounded).
- usage/anthropic: parse_iso8601 was stripping the UTC offset without
  applying it — negative-offset users saw countdowns up to 14h wrong.
  Now parses signed minutes and computes utc_secs = local - off*60.
  Also rejects y<1970, mo∉[1,12], d∉[1,max_day(mo,y)] up front so
  malformed API responses can't index DAYS_IN_MONTH out of bounds.
- usage: clamp utilization to [0,100] at all four Window construction
  sites so a misbehaving server can't render "121%".
- bubble: GetDC and CreateCompatibleDC results weren't checked. Guard
  both; release the screen DC on the CreateCompatibleDC failure path.

Refactor:
- Drop type TrayIconKind = ProviderId aliases (5 sites); use ProviderId
  directly everywhere. Inline the identity-function kind_to_provider.
- Delete panel::bar_color_for shim (was just argument-reorder glue).
- Replace local scale_to_dpi fns in bubble.rs and panel.rs with
  use crate::os::dpi::scale as scale_to_dpi (brings os::dpi into the
  live import graph; was unused before).
- Delete dead PCWSTR import + #[allow(dead_code)] sentinel in
  tray/badge.rs; fold the trailing `use BOOL` into the top imports.
- Inline app::primary_dpi() to crate::os::dpi::for_system().

app.rs:
- Add update_settings(|s: &mut AppState|) helper that locks state,
  runs the closure, snapshots Settings, drops the lock, then saves
  to disk. Convert four pure-mutate-then-save callsites.

Layering: bubble.rs no longer reaches upward into crate::app::.
Introduce bubble::Callbacks (fn-pointers), OnceLock<Callbacks>, and
bubble::install_callbacks(). The wnd_proc dispatches the six prior
upward calls via a private dispatch() helper. app::run installs
callbacks once at startup; the six on_bubble_* / recheck_theme fns
are demoted from pub fn to fn.

Resource-warning logs added: dispatch() warns on uninstalled
callbacks; app_icons() warns when ExtractIconExW returns nulls.

Build: cargo build --release clean; cargo clippy reports zero new
warnings (11 pre-existing, all in untouched code).
2026-05-23 11:22:40 +07:00

56 lines
1.7 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Parse Anthropic rate-limit headers into `UsageWindows`.
//
// The Messages API returns the user's remaining quota in response headers
// when the dedicated usage endpoint isn't available. Header names:
// anthropic-ratelimit-unified-5h-utilization (0.01.0)
// anthropic-ratelimit-unified-5h-reset (Unix seconds)
// anthropic-ratelimit-unified-7d-utilization
// anthropic-ratelimit-unified-7d-reset
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use crate::net::Response;
use crate::usage::{UsageWindows, Window};
pub fn parse_anthropic(response: &Response) -> UsageWindows {
UsageWindows {
primary: Window {
utilization: (header_f64(response, "anthropic-ratelimit-unified-5h-utilization")
* 100.0)
.clamp(0.0, 100.0),
resets_at: unix_to_systemtime(header_i64(
response,
"anthropic-ratelimit-unified-5h-reset",
)),
},
secondary: Window {
utilization: (header_f64(response, "anthropic-ratelimit-unified-7d-utilization")
* 100.0)
.clamp(0.0, 100.0),
resets_at: unix_to_systemtime(header_i64(
response,
"anthropic-ratelimit-unified-7d-reset",
)),
},
}
}
fn header_f64(response: &Response, name: &str) -> f64 {
response
.header(name)
.and_then(|s| s.parse().ok())
.unwrap_or(0.0)
}
fn header_i64(response: &Response, name: &str) -> Option<i64> {
response.header(name).and_then(|s| s.parse().ok())
}
fn unix_to_systemtime(secs: Option<i64>) -> Option<SystemTime> {
let s = secs?;
if s < 0 {
return None;
}
Some(UNIX_EPOCH + Duration::from_secs(s as u64))
}