mirror of
https://github.com/0xrsydn/idx-cli.git
synced 2026-11-07 01:35:36 +00:00
Phase 1 of docs/DESIGN.md: - Output format resolves -o > IDX_OUTPUT > general.output > auto; auto is a table on a terminal and JSON when stdout is piped or captured. `config init` now writes output = "auto". - JSON is pretty only on a terminal and compact otherwise. New `-o jsonl` prints one array item per line with the same key order. - No ANSI color when stdout is not a terminal (direct owo-colors calls now go through output::style). - `stocks quote -` and `stocks compare -` read symbols from stdin. - Exit codes by class: 2 invalid input/config, 3 not found or cache miss, 4 upstream unavailable, 5 unsupported, 1 other. Golden changes are limited to those documented kinds and were verified mechanically: JSON goldens are semantically identical, tables differ only by removed ANSI codes and the column padding comfy-table had reserved for them (cell contents identical), exit codes match each error's class, and help/completions only gain the new --output values. 17 new golden cases cover auto, jsonl, stdin, and the new errors. tests/cli.rs now pins IDX_OUTPUT=table in its harness (its tests assert table text and run piped) and parses discover JSON instead of matching pretty-printed whitespace. Live A/B against main: 16/16 stocks cases identical; ownership discover got IDX 403 on both binaries, now exiting 4 instead of 1 as intended. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
136 lines
3.8 KiB
Rust
136 lines
3.8 KiB
Rust
pub mod analysis;
|
|
mod api;
|
|
mod cache;
|
|
mod cli;
|
|
mod config;
|
|
mod curl_impersonate;
|
|
mod error;
|
|
mod output;
|
|
#[cfg(feature = "ownership")]
|
|
pub mod ownership;
|
|
|
|
pub mod runtime {
|
|
use std::fmt::Display;
|
|
use std::sync::atomic::{AtomicBool, Ordering};
|
|
|
|
static QUIET: AtomicBool = AtomicBool::new(false);
|
|
|
|
pub fn set_quiet(quiet: bool) {
|
|
QUIET.store(quiet, Ordering::Relaxed);
|
|
}
|
|
|
|
pub fn is_quiet() -> bool {
|
|
QUIET.load(Ordering::Relaxed)
|
|
}
|
|
|
|
pub fn info(message: impl Display) {
|
|
if !is_quiet() {
|
|
eprintln!("info: {message}");
|
|
}
|
|
}
|
|
|
|
pub fn warn(message: impl Display) {
|
|
if !is_quiet() {
|
|
eprintln!("warning: {message}");
|
|
}
|
|
}
|
|
}
|
|
|
|
use clap::CommandFactory;
|
|
use clap::Parser;
|
|
use clap_complete::{generate, shells};
|
|
|
|
use crate::api::default_provider;
|
|
use crate::cli::{Cli, Commands};
|
|
use crate::config::IdxConfig;
|
|
use crate::error::IdxError;
|
|
use crate::output::{OutputFormat, OutputSetting, emit_error};
|
|
|
|
fn main() {
|
|
if let Err(err) = run() {
|
|
std::process::exit(err.exit_code());
|
|
}
|
|
}
|
|
|
|
fn run() -> Result<(), IdxError> {
|
|
let cli = Cli::parse();
|
|
runtime::set_quiet(cli.quiet);
|
|
let config = match IdxConfig::load_with_cli(&cli) {
|
|
Ok(config) => config,
|
|
Err(err) => {
|
|
emit_error(&err, &bootstrap_output_format(&cli));
|
|
return Err(err);
|
|
}
|
|
};
|
|
output::json::configure(config.output, config::stdout_is_terminal());
|
|
output::style::set_color(!config.no_color);
|
|
|
|
match &cli.command {
|
|
Commands::Version => {
|
|
if config.output.is_json() {
|
|
output::json::print_json(&serde_json::json!({
|
|
"version": env!("CARGO_PKG_VERSION")
|
|
}))?;
|
|
} else {
|
|
println!("{}", env!("CARGO_PKG_VERSION"));
|
|
}
|
|
}
|
|
Commands::Completions { shell } => {
|
|
let mut cmd = Cli::command();
|
|
let name = cmd.get_name().to_owned();
|
|
match shell {
|
|
cli::Shell::Bash => generate(shells::Bash, &mut cmd, name, &mut std::io::stdout()),
|
|
cli::Shell::Zsh => generate(shells::Zsh, &mut cmd, name, &mut std::io::stdout()),
|
|
cli::Shell::Fish => generate(shells::Fish, &mut cmd, name, &mut std::io::stdout()),
|
|
}
|
|
}
|
|
Commands::Stocks(stocks) => {
|
|
let provider = default_provider(config.provider, cli.verbose > 0);
|
|
if let Err(err) = cli::stocks::handle(
|
|
stocks,
|
|
&config,
|
|
&provider,
|
|
cli.offline,
|
|
cli.no_cache,
|
|
cli.verbose > 0,
|
|
) {
|
|
emit_error(&err, &config.output);
|
|
return Err(err);
|
|
}
|
|
}
|
|
Commands::Config(cfg) => {
|
|
if let Err(err) = cli::config::handle(cfg) {
|
|
emit_error(&err, &config.output);
|
|
return Err(err);
|
|
}
|
|
}
|
|
Commands::Cache(cache) => {
|
|
if let Err(err) = cli::cache::handle(cache, &config.output) {
|
|
emit_error(&err, &config.output);
|
|
return Err(err);
|
|
}
|
|
}
|
|
#[cfg(feature = "ownership")]
|
|
Commands::Ownership(cmd) => {
|
|
if let Err(err) = cli::ownership::handle(&cmd.command, &config) {
|
|
emit_error(&err, &config.output);
|
|
return Err(err);
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Output format for errors raised before the configuration has loaded.
|
|
fn bootstrap_output_format(cli: &Cli) -> OutputFormat {
|
|
let setting = cli
|
|
.output
|
|
.or_else(|| {
|
|
std::env::var("IDX_OUTPUT")
|
|
.ok()
|
|
.and_then(|value| OutputSetting::parse(&value))
|
|
})
|
|
.unwrap_or_default();
|
|
setting.resolve(config::stdout_is_terminal())
|
|
}
|