perf(cache): reuse auth state in memory

Cache parsed auth state per remote and validate it with on-disk
file metadata so repeated authenticated API calls can skip
redundant open/read/JSON parse work within one process.

Centralize cache load, persist, and removal helpers in the cache
module, reuse them from login, logout, and whoami, and update
the refresh path to persist structured cache data directly.

Add targeted cache tests for memory reuse, invalidation after
external writes, persist updates, and cache removal.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-05-07 15:49:01 +09:00
co-authored by Copilot
parent a67f9a72a6
commit 4e73766732
5 changed files with 353 additions and 160 deletions
+8 -6
View File
@@ -1,14 +1,14 @@
use serde::Deserialize;
use serde::{Deserialize, Serialize};
/// Access and refresh token pair stored in the login cache file.
#[derive(Deserialize, Clone)]
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)]
pub struct CacheToken {
pub access: String,
pub refresh: String,
}
/// Minimal user fields stored in the cache, matching Python's `User` dataclass.
#[derive(Deserialize, Clone)]
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)]
pub struct CacheUser {
pub id: u32,
pub username: String,
@@ -17,7 +17,7 @@ pub struct CacheUser {
}
/// All four laboratory fields needed for digest computation.
#[derive(Deserialize, Clone)]
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)]
pub struct CacheLaboratory {
pub id: u32,
pub name: String,
@@ -28,15 +28,17 @@ pub struct CacheLaboratory {
}
/// Wrapper matching Python's `Laboratories` serialization: `{"items": [...]}`.
#[derive(Deserialize, Clone, Default)]
#[derive(Deserialize, Serialize, Clone, Debug, Default, PartialEq, Eq)]
pub struct CacheLabsWrapper {
pub items: Vec<CacheLaboratory>,
}
/// Full login cache, corresponding to the `<remote>.json` file written by `login`.
#[derive(Deserialize, Clone)]
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)]
pub struct Cache {
pub user: Option<CacheUser>,
pub token: CacheToken,
pub laboratories: CacheLabsWrapper,
#[serde(default)]
pub digest: String,
}