pub mod cache;
pub mod cascade;
pub mod credentials;
pub mod deps;
pub mod error;
pub mod fetcher;
pub mod git;
pub mod jsonl;
pub mod usage;
pub mod xdg;
use std::cell::OnceCell;
use std::path::PathBuf;
use std::sync::Arc;
use crate::input::StatusContext;
pub use credentials::{CredentialSource, Credentials};
pub use deps::DataDep;
pub use error::{
ClaudeJsonError, CredentialError, GitError, JsonlError, SessionError, SettingsError, UsageError,
};
pub use git::{DirtyCounts, DirtyState, GitContext, Head, RepoKind, UpstreamState};
pub use jsonl::{FiveHourBlock, JsonlAggregate, TokenCounts};
pub use usage::{
EndpointUsage, ExtraUsage, FiveHourWindow, JsonlUsage, SevenDayWindow, UsageApiResponse,
UsageBucket, UsageData,
};
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct Settings {}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct ClaudeJson {}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct LiveSessions {}
pub struct DataContext {
pub status: StatusContext,
cwd: Option<PathBuf>,
settings: OnceCell<Arc<Result<Settings, SettingsError>>>,
claude_json: OnceCell<Arc<Result<ClaudeJson, ClaudeJsonError>>>,
jsonl: OnceCell<Arc<Result<JsonlAggregate, JsonlError>>>,
usage: OnceCell<Arc<Result<UsageData, UsageError>>>,
credentials: OnceCell<Arc<Result<Credentials, CredentialError>>>,
sessions: OnceCell<Arc<Result<LiveSessions, SessionError>>>,
git: OnceCell<Arc<Result<Option<GitContext>, GitError>>>,
}
impl DataContext {
#[must_use]
pub fn new(status: StatusContext) -> Self {
Self::with_cwd(status, None)
}
#[must_use]
pub fn with_cwd(status: StatusContext, cwd: Option<PathBuf>) -> Self {
Self {
status,
cwd,
settings: OnceCell::new(),
claude_json: OnceCell::new(),
jsonl: OnceCell::new(),
usage: OnceCell::new(),
credentials: OnceCell::new(),
sessions: OnceCell::new(),
git: OnceCell::new(),
}
}
#[must_use]
pub fn settings(&self) -> Arc<Result<Settings, SettingsError>> {
self.settings
.get_or_init(|| Arc::new(Err(SettingsError::NotImplemented)))
.clone()
}
#[must_use]
pub fn claude_json(&self) -> Arc<Result<ClaudeJson, ClaudeJsonError>> {
self.claude_json
.get_or_init(|| Arc::new(Err(ClaudeJsonError::NotImplemented)))
.clone()
}
#[must_use]
pub fn jsonl(&self) -> Arc<Result<JsonlAggregate, JsonlError>> {
self.jsonl
.get_or_init(|| Arc::new(jsonl::aggregate_jsonl()))
.clone()
}
#[must_use]
pub fn usage(&self) -> Arc<Result<UsageData, UsageError>> {
self.usage
.get_or_init(|| Arc::new(self.resolve_usage_default()))
.clone()
}
fn resolve_usage_default(&self) -> Result<UsageData, UsageError> {
let root = cache::default_root();
let cache_store = root.clone().map(cache::CacheStore::new);
let lock_store = root.map(cache::LockStore::new);
let transport = fetcher::UreqTransport::new();
let config = cascade::UsageCascadeConfig::default();
cascade::resolve_usage(
cache_store.as_ref(),
lock_store.as_ref(),
&transport,
&|| self.credentials(),
&|| match &*self.jsonl() {
Ok(agg) => Ok(agg.clone()),
Err(JsonlError::IoError { path, cause }) => {
crate::lsm_warn!(
"cascade: JSONL aggregator IoError at {}: {} ({cause}); surfacing the original endpoint error since JSONL is unavailable",
path.display(),
cause.kind(),
);
Err(JsonlError::NoEntries)
}
Err(JsonlError::ParseError { path, line, cause }) => {
crate::lsm_warn!(
"cascade: JSONL aggregator ParseError at {}:{line}: {cause}; surfacing the original endpoint error since JSONL is unavailable",
path.display(),
);
Err(JsonlError::NoEntries)
}
Err(_) => Err(JsonlError::NoEntries),
},
&jiff::Timestamp::now,
&config,
)
}
#[must_use]
pub fn credentials(&self) -> Arc<Result<Credentials, CredentialError>> {
self.credentials
.get_or_init(|| Arc::new(credentials::resolve_credentials()))
.clone()
}
pub fn preseed_usage(
&self,
result: Result<UsageData, UsageError>,
) -> Result<(), Arc<Result<UsageData, UsageError>>> {
self.usage.set(Arc::new(result))
}
#[must_use]
pub fn sessions(&self) -> Arc<Result<LiveSessions, SessionError>> {
self.sessions
.get_or_init(|| Arc::new(Err(SessionError::NotImplemented)))
.clone()
}
#[must_use]
pub fn git(&self) -> Arc<Result<Option<GitContext>, GitError>> {
#[allow(clippy::arc_with_non_send_sync)]
self.git
.get_or_init(|| {
let result = match &self.cwd {
Some(cwd) => git::resolve_repo(cwd),
None => Ok(None),
};
if let Err(err) = &result {
crate::lsm_warn!("git discovery failed: {err}");
}
Arc::new(result)
})
.clone()
}
#[allow(clippy::arc_with_non_send_sync)]
pub fn preseed_git(
&self,
result: Result<Option<GitContext>, GitError>,
) -> Result<(), Arc<Result<Option<GitContext>, GitError>>> {
self.git.set(Arc::new(result))
}
}