Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions docs/specs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,37 @@
| `-c, --min-count <N>` | 分析に必要な最小データ数 | 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以上
Expand Down
10 changes: 5 additions & 5 deletions lawkit-cli/tests/cmd/options.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
...

```

Expand Down
57 changes: 55 additions & 2 deletions lawkit-cli/tests/spec/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
24 changes: 22 additions & 2 deletions lawkit-cli/tests/spec/output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:"));
}

// =============================================================================
Expand Down
4 changes: 2 additions & 2 deletions lawkit-core/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ use crate::OutputFormat;
pub fn format_output<T: Serialize>(results: &[T], format: OutputFormat) -> Result<String> {
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();
Expand Down
4 changes: 2 additions & 2 deletions lawkit-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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}")),
}
}

Expand Down Expand Up @@ -677,7 +677,7 @@ fn generate_sample_data(config: &Value, _options: &LawkitOptions) -> Result<Vec<
.map(|x| x as f64)
.collect()
}
_ => return Err(anyhow!("Unknown data type for generation: {}", data_type)),
_ => return Err(anyhow!("Unknown data type for generation: {data_type}")),
};

let generated_info = GeneratedDataInfo {
Expand Down
2 changes: 1 addition & 1 deletion lawkit-core/src/parsers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub fn parse_csv(content: &str) -> Result<Value> {

/// Parse YAML content - FOR INTERNAL USE ONLY
pub fn parse_yaml(content: &str) -> Result<Value> {
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
Expand Down
2 changes: 1 addition & 1 deletion lawkit-core/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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}")),
}
}
}
Expand Down
Loading