mirror of
https://github.com/0xrsydn/idx-cli.git
synced 2026-11-07 09:45:36 +00:00
fix(cli): keep total-failure JSON errors parseable
Remove obsolete unsupported-import diagnostics and defer comparison warnings until partial success. Preserve per-symbol diagnostics and quiet behavior when results remain available.
This commit is contained in:
parent
301e52ca83
commit
b4dcfceef6
3 changed files with 20 additions and 30 deletions
|
|
@ -137,6 +137,7 @@ CLI flags > environment variables > config file > defaults
|
||||||
- Table mode: human-readable error on stderr
|
- Table mode: human-readable error on stderr
|
||||||
- JSON mode: `{"error": true, "code": "...", "message": "..."}`
|
- JSON mode: `{"error": true, "code": "...", "message": "..."}`
|
||||||
- Exit code 0 on success, non-zero on failure
|
- Exit code 0 on success, non-zero on failure
|
||||||
|
- A totally failed `stocks compare` emits one structured error in JSON mode; per-symbol warnings are retained for partial success, not printed ahead of a total-failure error.
|
||||||
|
|
||||||
## CLI Output and Offline Cache
|
## CLI Output and Offline Cache
|
||||||
- `version`, `cache info`, `cache clear`, and `ownership resolve map|merge` emit
|
- `version`, `cache info`, `cache clear`, and `ownership resolve map|merge` emit
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ use crate::ownership::types::{
|
||||||
ChangeType, FlowSignal, HolderRow, KseiHolding, OwnershipRelease, OwnershipSource,
|
ChangeType, FlowSignal, HolderRow, KseiHolding, OwnershipRelease, OwnershipSource,
|
||||||
};
|
};
|
||||||
use crate::ownership::{archive, db, entities, graph, parser, remote, search, snapshot, xlsx};
|
use crate::ownership::{archive, db, entities, graph, parser, remote, search, snapshot, xlsx};
|
||||||
use crate::runtime;
|
|
||||||
|
|
||||||
#[derive(Debug, Args)]
|
#[derive(Debug, Args)]
|
||||||
#[command(
|
#[command(
|
||||||
|
|
@ -786,28 +785,15 @@ fn handle_import(args: &ImportArgs, config: &IdxConfig) -> Result<(), IdxError>
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(symbols) = &args.fetch_bing {
|
if args.fetch_bing.as_ref().is_some_and(|symbols| {
|
||||||
let clean: Vec<String> = symbols
|
symbols
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|v| v.split(','))
|
.flat_map(|value| value.split(','))
|
||||||
.map(str::trim)
|
.any(|value| !value.trim().is_empty())
|
||||||
.filter(|s| !s.is_empty())
|
}) {
|
||||||
.map(|s| s.to_uppercase())
|
return Err(IdxError::Unsupported(
|
||||||
.collect();
|
"--fetch-bing import is not implemented yet".to_string(),
|
||||||
|
));
|
||||||
if !clean.is_empty() {
|
|
||||||
runtime::info(format!(
|
|
||||||
"--fetch-bing requested for {} symbol(s), implementation deferred for Sprint 6",
|
|
||||||
clean.len()
|
|
||||||
));
|
|
||||||
for symbol in &clean {
|
|
||||||
runtime::info(format!(" - {symbol}"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Err(IdxError::Unsupported(
|
|
||||||
"--fetch-bing import is not implemented yet".to_string(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(import_input) = resolve_import_input(args)? else {
|
let Some(import_input) = resolve_import_input(args)? else {
|
||||||
|
|
|
||||||
|
|
@ -647,7 +647,7 @@ pub fn handle(
|
||||||
}
|
}
|
||||||
StocksSubcommand::Compare { symbols } => {
|
StocksSubcommand::Compare { symbols } => {
|
||||||
let mut reports: Vec<FundamentalReport> = Vec::new();
|
let mut reports: Vec<FundamentalReport> = Vec::new();
|
||||||
let mut last_error = None;
|
let mut failures = Vec::new();
|
||||||
|
|
||||||
for sym in symbols.iter().flat_map(|s| s.split(',')) {
|
for sym in symbols.iter().flat_map(|s| s.split(',')) {
|
||||||
let resolved = crate::api::resolve_symbol(sym, &config.exchange)?;
|
let resolved = crate::api::resolve_symbol(sym, &config.exchange)?;
|
||||||
|
|
@ -664,21 +664,24 @@ pub fn handle(
|
||||||
analyze_fundamental,
|
analyze_fundamental,
|
||||||
) {
|
) {
|
||||||
Ok(report) => reports.push(report),
|
Ok(report) => reports.push(report),
|
||||||
Err(err) => {
|
Err(err) => failures.push((resolved, err)),
|
||||||
runtime::warn(format!(
|
|
||||||
"failed to fetch fundamentals for {resolved}: {err}"
|
|
||||||
));
|
|
||||||
last_error = Some(err);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if reports.is_empty() {
|
if reports.is_empty() {
|
||||||
return Err(last_error.unwrap_or_else(|| {
|
return Err(failures.pop().map(|(_, err)| err).unwrap_or_else(|| {
|
||||||
IdxError::CacheMiss("fundamental/no symbols could be compared".to_string())
|
IdxError::CacheMiss("fundamental/no symbols could be compared".to_string())
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Partial success needs diagnostics; total failure must leave stderr
|
||||||
|
// available for the single structured error emitted by the caller.
|
||||||
|
for (resolved, err) in failures {
|
||||||
|
runtime::warn(format!(
|
||||||
|
"failed to fetch fundamentals for {resolved}: {err}"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
render_compare(&reports, &config.output, config.no_color)
|
render_compare(&reports, &config.output, config.no_color)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue