diff --git a/docs/specs/cli.md b/docs/specs/cli.md index 66a7a19..53f98c0 100644 --- a/docs/specs/cli.md +++ b/docs/specs/cli.md @@ -51,6 +51,37 @@ | `-c, --min-count ` | 分析に必要な最小データ数 | 10 | | `--no-color` | 色付け無効化 | - | +### `-v, --verbose` + +処理の詳細情報を stderr に、追加統計を stdout に表示する。 + +以下は `benf` サブコマンドの例。stderr のデバッグ情報は全サブコマンド共通、stdout の追加統計はサブコマンドにより異なる。 + +**stderr出力(デバッグ情報)**: +``` +Debug: input argument = None +Debug: Reading from stdin, using automatic optimization +Debug: Using automatic optimization (streaming + incremental + memory efficiency) +Debug: Collected 30 numbers from stream +Debug: Streaming analysis successful - 30 items processed +Debug: Processed 30 numbers in 1 chunks +Debug: Memory used: 0.00 MB +Debug: Processing time: 0 ms +``` + +**stdout出力(追加統計)**: +``` +First Digit Distribution: +1: 36.7% (expected: 30.1%, deviation: +6.6%) +2: 36.7% (expected: 17.6%, deviation: +19.1%) +... + +Statistical Tests: +Chi-square: 35.29 (p-value: 0.010000) +``` + +**注意**: デバッグ情報は stderr、分析結果は stdout に出力される。パイプ利用時に干渉しない。 + ### フィルタ構文 - `>=100` - 100以上 diff --git a/lawkit-cli/tests/cmd/options.md b/lawkit-cli/tests/cmd/options.md index c81c972..3099ab5 100644 --- a/lawkit-cli/tests/cmd/options.md +++ b/lawkit-cli/tests/cmd/options.md @@ -40,15 +40,15 @@ risk_level: Low ## Verbose Mode -Detailed output with additional statistics. +Debug info on stderr, additional statistics on stdout. ```console $ lawkit benf data.txt -v ... -Additional Statistics: - Mean Absolute Deviation: 0.002 - Kolmogorov-Smirnov: 0.045 - ... +First Digit Distribution: +... +Statistical Tests: +... ``` diff --git a/lawkit-cli/tests/spec/options.rs b/lawkit-cli/tests/spec/options.rs index 2945398..b64bb4f 100644 --- a/lawkit-cli/tests/spec/options.rs +++ b/lawkit-cli/tests/spec/options.rs @@ -43,14 +43,67 @@ fn test_quiet_long_option() { fn test_verbose_option() { let mut cmd = lawkit(); cmd.args(["benf", "-v"]).write_stdin(SAMPLE_DATA); - cmd.assert().code(valid_exit_codes()); + cmd.assert() + .code(valid_exit_codes()) + .stderr(predicate::str::contains("Debug:")) + .stdout(predicate::str::contains("deviation:")); } #[test] fn test_verbose_long_option() { let mut cmd = lawkit(); cmd.args(["benf", "--verbose"]).write_stdin(SAMPLE_DATA); - cmd.assert().code(valid_exit_codes()); + cmd.assert() + .code(valid_exit_codes()) + .stderr(predicate::str::contains("Debug:")) + .stdout(predicate::str::contains("deviation:")); +} + +#[test] +fn test_verbose_stderr_contains_debug_info() { + let mut cmd = lawkit(); + cmd.args(["benf", "-v"]).write_stdin(SAMPLE_DATA); + cmd.assert() + .code(valid_exit_codes()) + .stderr(predicate::str::contains("Streaming analysis successful")) + .stderr(predicate::str::contains("Memory used:")) + .stderr(predicate::str::contains("Processing time:")); +} + +#[test] +fn test_verbose_pareto() { + let mut cmd = lawkit(); + cmd.args(["pareto", "-v"]).write_stdin(SAMPLE_DATA); + cmd.assert() + .code(valid_exit_codes()) + .stderr(predicate::str::contains("Debug:")); +} + +#[test] +fn test_verbose_zipf() { + let mut cmd = lawkit(); + cmd.args(["zipf", "-v"]).write_stdin(SAMPLE_DATA); + cmd.assert() + .code(valid_exit_codes()) + .stderr(predicate::str::contains("Debug:")); +} + +#[test] +fn test_verbose_normal() { + let mut cmd = lawkit(); + cmd.args(["normal", "-v"]).write_stdin(SAMPLE_DATA); + cmd.assert() + .code(valid_exit_codes()) + .stderr(predicate::str::contains("Debug:")); +} + +#[test] +fn test_verbose_poisson() { + let mut cmd = lawkit(); + cmd.args(["poisson", "-v"]).write_stdin(SAMPLE_DATA); + cmd.assert() + .code(valid_exit_codes()) + .stderr(predicate::str::contains("Debug:")); } #[test] diff --git a/lawkit-cli/tests/spec/output.rs b/lawkit-cli/tests/spec/output.rs index b8ec10d..29d3cdd 100644 --- a/lawkit-cli/tests/spec/output.rs +++ b/lawkit-cli/tests/spec/output.rs @@ -88,12 +88,32 @@ fn output_quiet_short_form() { #[test] fn output_verbose() { - generate_sample().arg("--verbose").assert().success(); + generate_sample() + .arg("--verbose") + .assert() + .success() + .stdout(predicate::str::is_empty().not()); } #[test] fn output_verbose_short_form() { - generate_sample().arg("-v").assert().success(); + generate_sample() + .arg("-v") + .assert() + .success() + .stdout(predicate::str::is_empty().not()); +} + +#[test] +fn output_verbose_analysis_has_debug_stderr() { + // Analysis commands (not generate) should output debug info to stderr + let mut cmd = lawkit(); + cmd.args(["benf", "-v"]) + .write_stdin("10\n20\n30\n40\n50\n60\n70\n80\n90\n100\n110\n120\n130\n140\n150\n"); + cmd.assert() + .code(predicates::prelude::predicate::in_iter([0, 10, 11])) + .stderr(predicate::str::contains("Debug:")) + .stdout(predicate::str::contains("Statistical Tests:")); } // ============================================================================= diff --git a/lawkit-core/src/helpers.rs b/lawkit-core/src/helpers.rs index afdfa63..643d024 100644 --- a/lawkit-core/src/helpers.rs +++ b/lawkit-core/src/helpers.rs @@ -14,9 +14,9 @@ use crate::OutputFormat; pub fn format_output(results: &[T], format: OutputFormat) -> Result { match format { OutputFormat::Json => serde_json::to_string_pretty(results) - .map_err(|e| anyhow!("JSON serialization error: {}", e)), + .map_err(|e| anyhow!("JSON serialization error: {e}")), OutputFormat::Yaml => { - serde_yaml::to_string(results).map_err(|e| anyhow!("YAML serialization error: {}", e)) + serde_yaml::to_string(results).map_err(|e| anyhow!("YAML serialization error: {e}")) } OutputFormat::Lawkit => { let mut output = String::new(); diff --git a/lawkit-core/src/lib.rs b/lawkit-core/src/lib.rs index eb657f9..c979d86 100644 --- a/lawkit-core/src/lib.rs +++ b/lawkit-core/src/lib.rs @@ -35,7 +35,7 @@ pub fn law( "validate" => validate_data(data_or_config, opts), "diagnose" => diagnose_data(data_or_config, opts), "generate" => generate_sample_data(data_or_config, opts), - _ => Err(anyhow!("Unknown subcommand: {}", subcommand)), + _ => Err(anyhow!("Unknown subcommand: {subcommand}")), } } @@ -677,7 +677,7 @@ fn generate_sample_data(config: &Value, _options: &LawkitOptions) -> Result return Err(anyhow!("Unknown data type for generation: {}", data_type)), + _ => return Err(anyhow!("Unknown data type for generation: {data_type}")), }; let generated_info = GeneratedDataInfo { diff --git a/lawkit-core/src/parsers.rs b/lawkit-core/src/parsers.rs index 5179087..8aeeaa7 100644 --- a/lawkit-core/src/parsers.rs +++ b/lawkit-core/src/parsers.rs @@ -41,7 +41,7 @@ pub fn parse_csv(content: &str) -> Result { /// Parse YAML content - FOR INTERNAL USE ONLY pub fn parse_yaml(content: &str) -> Result { - serde_yaml::from_str(content).map_err(|e| anyhow!("YAML parse error: {}", e)) + serde_yaml::from_str(content).map_err(|e| anyhow!("YAML parse error: {e}")) } /// Parse TOML content - FOR INTERNAL USE ONLY diff --git a/lawkit-core/src/types.rs b/lawkit-core/src/types.rs index 7ab36c7..5a6e59e 100644 --- a/lawkit-core/src/types.rs +++ b/lawkit-core/src/types.rs @@ -153,7 +153,7 @@ impl OutputFormat { "yaml" | "yml" => Ok(Self::Yaml), "csv" => Ok(Self::Csv), "text" | "txt" => Ok(Self::Text), - _ => Err(anyhow!("Invalid output format: {}", s)), + _ => Err(anyhow!("Invalid output format: {s}")), } } }