mirror of
https://github.com/0xrsydn/idx-cli.git
synced 2026-08-07 09:43:53 +00:00
refactor: schema-driven architecture, capability traits, hardened error paths
- Split parse.rs into raw_types.rs (serde structs) + map.rs (pure transforms) for MSN and Yahoo - Replace Yahoo fundamentals dynamic HashMap with typed structs (SummaryDetail, DefaultKeyStatistics, etc.) - Introduce capability-based provider traits: QuoteProvider, FundamentalsProvider, HistoryProvider - Add future MSN capability traits: ProfileProvider, EarningsProvider, FinancialsProvider, SentimentProvider, InsightsProvider, NewsProvider (all dead_code until wired to CLI) - Add shared domain types in src/api/types.rs (CompanyProfile, EarningsReport, FinancialStatements, SentimentData, InsightData, NewsItem) - Harden error propagation: Yahoo cookie auth, MSN partial fundamentals, history symbol context, cache clear failures - Strict config parsing: invalid IDX_OUTPUT returns ConfigError instead of silent fallback - Cache schema version enforcement: version mismatch treated as cache miss - Extract fetch_with_cache() helper in cli/stocks.rs - Add MSN retry/backoff parity with Yahoo client - Standardize Option<T> policy through parse/map layers - All 56 tests passing, clippy clean
This commit is contained in:
parent
a45ee3620f
commit
3998e38ffc
17 changed files with 1185 additions and 969 deletions
33
src/cache.rs
33
src/cache.rs
|
|
@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize};
|
|||
|
||||
use crate::error::IdxError;
|
||||
|
||||
const SCHEMA_VERSION: u32 = 1;
|
||||
const CURRENT_SCHEMA_VERSION: u32 = 1;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Cache {
|
||||
|
|
@ -83,7 +83,7 @@ impl Cache {
|
|||
let entry = CacheEntry {
|
||||
fetched_at: Utc::now(),
|
||||
ttl_secs,
|
||||
schema_version: SCHEMA_VERSION,
|
||||
schema_version: CURRENT_SCHEMA_VERSION,
|
||||
data,
|
||||
};
|
||||
let raw = serde_json::to_string_pretty(&entry)
|
||||
|
|
@ -124,17 +124,21 @@ impl Cache {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn clear(&self) -> Result<usize, IdxError> {
|
||||
pub fn clear(&self) -> Result<(usize, Vec<PathBuf>), IdxError> {
|
||||
if !self.root.exists() {
|
||||
return Ok(0);
|
||||
return Ok((0, Vec::new()));
|
||||
}
|
||||
let mut removed = 0usize;
|
||||
let mut failed = Vec::new();
|
||||
self.walk(&self.root, &mut |p| {
|
||||
if p.is_file() && fs::remove_file(p).is_ok() {
|
||||
removed += 1;
|
||||
if p.is_file() {
|
||||
match fs::remove_file(p) {
|
||||
Ok(_) => removed += 1,
|
||||
Err(_) => failed.push(p.to_path_buf()),
|
||||
}
|
||||
}
|
||||
})?;
|
||||
Ok(removed)
|
||||
Ok((removed, failed))
|
||||
}
|
||||
|
||||
fn walk<F: FnMut(&Path)>(&self, dir: &Path, f: &mut F) -> Result<(), IdxError> {
|
||||
|
|
@ -159,8 +163,19 @@ impl Cache {
|
|||
if !path.exists() {
|
||||
return Ok(None);
|
||||
}
|
||||
let raw = fs::read_to_string(path).map_err(|e| IdxError::Io(e.to_string()))?;
|
||||
let entry = serde_json::from_str(&raw).map_err(|e| IdxError::ParseError(e.to_string()))?;
|
||||
let raw = fs::read_to_string(&path).map_err(|e| IdxError::Io(e.to_string()))?;
|
||||
let entry: CacheEntry<T> =
|
||||
serde_json::from_str(&raw).map_err(|e| IdxError::ParseError(e.to_string()))?;
|
||||
if entry.schema_version != CURRENT_SCHEMA_VERSION {
|
||||
eprintln!(
|
||||
"debug: cache schema mismatch for {} (got {}, expected {})",
|
||||
path.display(),
|
||||
entry.schema_version,
|
||||
CURRENT_SCHEMA_VERSION
|
||||
);
|
||||
let _ = fs::remove_file(&path);
|
||||
return Ok(None);
|
||||
}
|
||||
Ok(Some(entry))
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue