pub struct Cache { /* private fields */ }Expand description
Per-vendor cache directory and helper API.
Construct with Cache::for_vendor; the directory is created lazily.
Implementations§
Source§impl Cache
impl Cache
Sourcepub fn for_vendor(vendor: &str) -> Result<Self>
pub fn for_vendor(vendor: &str) -> Result<Self>
Build a cache rooted at ~/.cache/ai-usagebar/<vendor> (or under
$XDG_CACHE_HOME when set).
Sourcepub fn for_vendor_account(vendor: &str, label: &str) -> Result<Self>
pub fn for_vendor_account(vendor: &str, label: &str) -> Result<Self>
Cache for a specific named account of a vendor, rooted at
~/.cache/ai-usagebar/<vendor>/<label>. Only extra accounts use
this; the default account keeps Cache::for_vendor so its path never
moves (issue #14, back-compat rule 2).
Sourcepub fn ensure_dir(&self) -> Result<()>
pub fn ensure_dir(&self) -> Result<()>
Ensure the directory exists. Safe to call repeatedly.
pub fn dir(&self) -> &Path
pub fn payload_path(&self) -> PathBuf
pub fn stale_path(&self) -> PathBuf
pub fn last_error_path(&self) -> PathBuf
pub fn lock_path(&self) -> PathBuf
Sourcepub fn retry_after_path(&self) -> PathBuf
pub fn retry_after_path(&self) -> PathBuf
Rate-limit backoff marker: unix epoch seconds (plain decimal text) before which no request should be made to this vendor.
Sourcepub fn payload_age(&self) -> Option<Duration>
pub fn payload_age(&self) -> Option<Duration>
Age of the payload (None if it doesn’t exist). Used by the widget to
decide whether the 60s cache window applies.
Sourcepub fn fresh_payload(&self, ttl: Duration) -> Result<Option<Vec<u8>>>
pub fn fresh_payload(&self, ttl: Duration) -> Result<Option<Vec<u8>>>
Returns the cached payload only if it is younger than ttl. Used as
the fast path in _fetch_usage (claudebar:343-349).
This is the one pre-network hook shared by every vendor: each
fetch_snapshot calls it before opening a connection and records HTTP
failures through Cache::write_last_error. That makes this the single
place a cross-vendor request policy can live without nineteen private
copies drifting apart — which is why the rate-limit backoff is applied
here rather than in each vendor.
Policy, in order:
- While a 429 backoff is armed (
Cache::backoff_remaining_at), no request is made. A payload still insideMAX_STALEis served as the answer, TTL notwithstanding, so the bar keeps its last good figure without touching the network. With nothing worth showing this returns anAppError::Httpwith status 429 whose body names the time until the next attempt; the vendor’s?propagates it and the network is never reached..last_erroris left as the vendor wrote it. - Otherwise the ordinary TTL check runs unchanged.
Sourcepub fn fresh_payload_at(
&self,
ttl: Duration,
now: SystemTime,
) -> Result<Option<Vec<u8>>>
pub fn fresh_payload_at( &self, ttl: Duration, now: SystemTime, ) -> Result<Option<Vec<u8>>>
Cache::fresh_payload with an injected clock for the backoff check.
The TTL comparison still reads the payload’s mtime against the real
clock via Cache::payload_age.
Sourcepub fn note_rate_limit_at(&self, now: SystemTime)
pub fn note_rate_limit_at(&self, now: SystemTime)
Arm the rate-limit backoff: no request until now + RATE_LIMIT_BACKOFF.
Best-effort, never errors — a cache dir that cannot be written costs a
retry, not a crash.
Sourcepub fn clear_backoff(&self)
pub fn clear_backoff(&self)
Best-effort removal of the backoff marker. A successful payload write
and an explicit clear_last_error both end the backoff.
Sourcepub fn backoff_remaining_at(&self, now: SystemTime) -> Option<Duration>
pub fn backoff_remaining_at(&self, now: SystemTime) -> Option<Duration>
Time left on an armed backoff, as of now. None when the marker is
missing, unparseable, or already in the past — a corrupt marker must
never pin a vendor offline.
Sourcepub fn backoff_remaining(&self) -> Option<Duration>
pub fn backoff_remaining(&self) -> Option<Duration>
Cache::backoff_remaining_at against the real clock.
Sourcepub fn maybe_payload(&self) -> Result<Option<Vec<u8>>>
pub fn maybe_payload(&self) -> Result<Option<Vec<u8>>>
Read the payload regardless of age. Err if the file exists but is
unreadable; Ok(None) if it just doesn’t exist.
Prefer Cache::fallback_payload on failure paths — this one imposes
no age limit, so it will happily hand back a month-old figure.
Sourcepub fn fallback_payload(&self, max_stale: Duration) -> Result<Option<Vec<u8>>>
pub fn fallback_payload(&self, max_stale: Duration) -> Result<Option<Vec<u8>>>
Payload for the failure path: the last good value, but only while it
is still worth showing. Beyond max_stale this returns Ok(None) so
the caller surfaces the real error instead of presenting week-old
numbers as if they were current — a bar that silently freezes on
history is worse than one that says it cannot reach the API.
Sourcepub fn write_payload(&self, bytes: &[u8]) -> Result<()>
pub fn write_payload(&self, bytes: &[u8]) -> Result<()>
Atomically write a new payload. Uses tempfile + persist (POSIX
rename), matching claudebar’s mktemp + mv invariant.
Sourcepub fn mark_stale(&self)
pub fn mark_stale(&self)
Mark the cache as stale. Idempotent.
pub fn is_stale(&self) -> bool
Sourcepub fn write_last_error(&self, code: u16, msg: &str) -> (u16, String)
pub fn write_last_error(&self, code: u16, msg: &str) -> (u16, String)
Write the .last_error marker — first line code, everything after it
msg. Best-effort, never errors (matches claudebar:478-486 which
silently continues if the cache dir isn’t writable).
Returns exactly what was written, so a caller that also puts the
failure in its crate::vendor::VendorOutcome can hand over this pair
instead of deriving a second one from the raw body. The two must not be
computed separately: persisting a redacted message while the in-memory
copy kept the original is how a 401 body reached the widget tooltip on
the one run that had a warm cache to fall back on. Callers that only
persist can keep ignoring the return.
Sourcepub fn clear_last_error(&self)
pub fn clear_last_error(&self)
Best-effort removal of the .last_error marker, and of the backoff
that a 429 among those errors may have armed.
pub fn read_last_error(&self) -> Option<(u16, String)>
Trait Implementations§
Auto Trait Implementations§
impl Freeze for Cache
impl RefUnwindSafe for Cache
impl Send for Cache
impl Sync for Cache
impl Unpin for Cache
impl UnsafeUnpin for Cache
impl UnwindSafe for Cache
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more