mirror of
https://github.com/0xrsydn/idx-cli.git
synced 2026-11-07 09:45:36 +00:00
feat(cache): add file cache, cache commands, and offline fallback
This commit is contained in:
parent
d2fa081266
commit
c0a3d744b2
3 changed files with 296 additions and 4 deletions
|
|
@ -1,5 +1,8 @@
|
|||
use clap::{Args, Subcommand};
|
||||
|
||||
use crate::cache::Cache;
|
||||
use crate::error::IdxError;
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
pub struct CacheCmd {
|
||||
#[command(subcommand)]
|
||||
|
|
@ -11,3 +14,32 @@ pub enum CacheSubcommand {
|
|||
Info,
|
||||
Clear,
|
||||
}
|
||||
|
||||
pub fn handle(cmd: &CacheCmd) -> Result<(), IdxError> {
|
||||
let cache = Cache::new()?;
|
||||
match &cmd.command {
|
||||
CacheSubcommand::Info => {
|
||||
let info = cache.info()?;
|
||||
println!("path: {}", info.path.display());
|
||||
println!("files: {}", info.files);
|
||||
println!("size_bytes: {}", info.total_size);
|
||||
println!(
|
||||
"oldest: {}",
|
||||
info.oldest
|
||||
.map(|v| v.to_rfc3339())
|
||||
.unwrap_or_else(|| "-".to_string())
|
||||
);
|
||||
println!(
|
||||
"newest: {}",
|
||||
info.newest
|
||||
.map(|v| v.to_rfc3339())
|
||||
.unwrap_or_else(|| "-".to_string())
|
||||
);
|
||||
}
|
||||
CacheSubcommand::Clear => {
|
||||
let removed = cache.clear()?;
|
||||
println!("cleared {removed} files");
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use clap::{Args, Subcommand};
|
|||
|
||||
use crate::api::types::{Interval, Period};
|
||||
use crate::api::MarketDataProvider;
|
||||
use crate::cache::Cache;
|
||||
use crate::config::IdxConfig;
|
||||
use crate::error::IdxError;
|
||||
use crate::output::{render_history, render_quotes};
|
||||
|
|
@ -28,13 +29,48 @@ pub fn handle(
|
|||
cmd: &StocksCmd,
|
||||
config: &IdxConfig,
|
||||
provider: &dyn MarketDataProvider,
|
||||
offline: bool,
|
||||
no_cache: bool,
|
||||
) -> Result<(), IdxError> {
|
||||
let cache = Cache::new()?;
|
||||
|
||||
match &cmd.command {
|
||||
StocksSubcommand::Quote { symbols } => {
|
||||
let mut quotes = Vec::new();
|
||||
for sym in symbols.iter().flat_map(|s| s.split(',')) {
|
||||
let resolved = crate::api::resolve_symbol(sym, &config.exchange);
|
||||
quotes.push(provider.quote(&resolved)?);
|
||||
if !no_cache
|
||||
&& let Some(q) = cache.get("quote", &resolved)?
|
||||
{
|
||||
quotes.push(q);
|
||||
continue;
|
||||
}
|
||||
if offline {
|
||||
let stale = cache
|
||||
.get_stale("quote", &resolved)?
|
||||
.ok_or_else(|| IdxError::CacheMiss(format!("quote/{resolved}")))?;
|
||||
quotes.push(stale);
|
||||
continue;
|
||||
}
|
||||
|
||||
match provider.quote(&resolved) {
|
||||
Ok(q) => {
|
||||
if !no_cache {
|
||||
cache.put("quote", &resolved, &q, config.quote_ttl)?;
|
||||
}
|
||||
quotes.push(q);
|
||||
}
|
||||
Err(err) => {
|
||||
if !no_cache
|
||||
&& let Some(stale) = cache.get_stale("quote", &resolved)?
|
||||
{
|
||||
eprintln!("warning: network failed, serving stale cache for {resolved}");
|
||||
quotes.push(stale);
|
||||
continue;
|
||||
}
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
render_quotes("es, &config.output, config.no_color)
|
||||
}
|
||||
|
|
@ -44,8 +80,36 @@ pub fn handle(
|
|||
interval,
|
||||
} => {
|
||||
let resolved = crate::api::resolve_symbol(symbol, &config.exchange);
|
||||
let history = provider.history(&resolved, period, interval)?;
|
||||
render_history(&resolved, &history, &config.output)
|
||||
let key = format!("{}-{}", period.as_str(), interval.as_str());
|
||||
if !no_cache
|
||||
&& let Some(history) = cache.get::<Vec<crate::api::types::Ohlc>>("history", &format!("{resolved}-{key}"))?
|
||||
{
|
||||
return render_history(&resolved, &history, &config.output);
|
||||
}
|
||||
if offline {
|
||||
let stale = cache
|
||||
.get_stale::<Vec<crate::api::types::Ohlc>>("history", &format!("{resolved}-{key}"))?
|
||||
.ok_or_else(|| IdxError::CacheMiss(format!("history/{resolved}-{key}")))?;
|
||||
return render_history(&resolved, &stale, &config.output);
|
||||
}
|
||||
|
||||
match provider.history(&resolved, period, interval) {
|
||||
Ok(history) => {
|
||||
if !no_cache {
|
||||
cache.put("history", &format!("{resolved}-{key}"), &history, config.quote_ttl)?;
|
||||
}
|
||||
render_history(&resolved, &history, &config.output)
|
||||
}
|
||||
Err(err) => {
|
||||
if !no_cache
|
||||
&& let Some(stale) = cache.get_stale::<Vec<crate::api::types::Ohlc>>("history", &format!("{resolved}-{key}"))?
|
||||
{
|
||||
eprintln!("warning: network failed, serving stale cache for {resolved}");
|
||||
return render_history(&resolved, &stale, &config.output);
|
||||
}
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue