**English** | [日本語](docs/README.jp.md)

# hanuke

A CLI tool that checks CSV columns for consecutive sequences.

It verifies, in order of appearance, that each row equals "previous value +
step", and detects gaps, duplicates, and order violations (backward moves or
values off the step grid). Following the UNIX philosophy, it prints nothing by
default and reports the result via the exit code alone, which makes it easy to
embed in pipelines and shell scripts.

## Install

```console
$ cargo install hanuke                # from crates.io
```

Prebuilt binaries for Windows / Linux / macOS (and Android) are attached to each
[GitHub release](https://github.com/ShortArrow/hanuke/releases): download the
archive for your target and put `hanuke` on your `PATH`. Release binaries embed
the source commit (`hanuke version --commit`); `cargo install` builds report
`unknown` because no git tree is present at build time.

## Exit codes

| code | meaning |
|------|---------|
| 0    | Sequence is valid (empty files and single rows count as valid) |
| 1    | Gaps, duplicates, or order violations detected (by default a one-line summary goes to stderr) |
| 2    | Argument, file I/O, or parse error |

## Usage

```console
$ hanuke data.csv                     # check the first column, decimal, step +1
$ hanuke --header data.csv            # skip the header row
$ hanuke --col 3 data.csv             # check the 3rd column (1-based)
$ hanuke --hex data.csv               # hexadecimal ("0x" prefix accepted)
$ hanuke --step -1 data.csv           # check a descending sequence
$ cat data.csv | hanuke -             # read from stdin
```

### Version information

```console
$ hanuke --version                    # or: hanuke version
hanuke 0.1.0
$ hanuke version --commit             # append the git commit the binary was built from
hanuke 0.1.0 (fa49922d9b5f68b91759136dd96b1ff153260469)
```

`--version` / `-V` and the `version` subcommand print the same line; the commit
hash is available via `version --commit` (shown as `unknown` if the binary was
built without git available).

### Supported CSV patterns

1. **Multiple fixed-byte-width hex columns** (LE/BE): `--hex --cols N --endian le|be`
2. **The decimal version of the above**: `--cols N` (each column is 1 byte = 0..255; change with `--col-bytes`)
3. **A single `0x`-prefixed hex column**: `--hex`
4. **A single unprefixed hex column**: `--hex`
5. **A single int / uint / integer-valued double column**: default (`3.0` and `1e3` are accepted as integers, `3.5` is an error)

Multi-column mode treats each record as one value split into byte-sized
columns, combines them according to `--endian`, and compares the result as a
single integer. For hex, the byte width per column is inferred from the digit
count of the first data row (specify `--col-bytes` to be explicit).

```console
# 00,FE / 00,FF / 01,00 (big endian) → 0x00FE, 0x00FF, 0x0100
$ hanuke --hex --cols 2 --endian be data.csv
```

### Detailed output

```console
$ hanuke --verbose data.csv
rows: 4
start: 0
end: 5
missing: 2..4
duplicates: 5
issue: row 3: gap: expected 2, got 5 (missing 2..4)
issue: row 4: duplicate value 5
result: NG (2 issue(s))
```

For machine-readable output use `--json` (a single JSON document regardless of
OK/NG; exit codes work as usual, so it combines well with `jq`). On its own it
prints a size-bounded summary containing **counts only**:

```console
$ hanuke --json data.csv
{"ok":false,"rows":4,"start":0,"end":5,
 "issue_count":{"total":2,"gap":1,"duplicate":1,"order_violation":0},"missing_count":3}
```

Adding `--verbose` includes the `missing` / `duplicates` / `issues` lists
(note that these grow with the number of issues):

```console
$ hanuke --json --verbose data.csv
{"ok":false,"rows":4,"start":0,"end":5,
 "issue_count":{"total":2,"gap":1,"duplicate":1,"order_violation":0},"missing_count":3,
 "missing":[{"first":2,"last":4,"count":3}],"duplicates":[5],
 "issues":[{"type":"gap","row":3,"expected":2,"actual":5,"missing":{"first":2,"last":4,"count":3}},
           {"type":"duplicate","row":4,"value":5}]}
```

The issue `type` is one of `gap` / `duplicate` / `order_violation`.
`start` and `end` are `null` when there are no data rows. `missing_count` is
the total number of missing values.

When you need just one piece of information, use `--show` (bare values are
printed, which is script-friendly):

```console
$ hanuke --show missing data.csv      # missing ranges, one per line (e.g. "5..7", inclusive)
$ hanuke --show duplicates data.csv   # duplicate values, one per line
$ hanuke --show start,end data.csv    # multiple kinds get labels like "start: 10"
```

## Options

| option | default | description |
|--------|---------|-------------|
| `-c, --col <N>` | 1 | First column to check (1-based) |
| `-n, --cols <N>` | 1 | Number of columns per record (2+ enables combine mode) |
| `-x, --hex` | off | Interpret values as hexadecimal |
| `--endian <le\|be>` | be | Byte order when combining multiple columns |
| `--col-bytes <N>` | inferred | Bytes per column in multi-column mode (1..=16) |
| `--header` | off | Skip the first row as a header |
| `--quote <C>` | `"` | Quote character (single ASCII char; `--quote "'"` for single quotes) |
| `-s, --step <N>` | 1 | Expected step (negative for descending; must not be 0) |
| `-v, --verbose` | off | Print the summary and issue details to stdout |
| `--show <KIND>` | none | Print only start / end / missing / duplicates (comma-separated) |
| `--json` | off | Print a JSON count summary; add `--verbose` for full lists (conflicts with `--show`) |

## Accepted input formats

- Line endings may be LF or CRLF (mixed is fine). A UTF-8 BOM is ignored
- Cell quoting follows RFC 4180 (embedded commas like `"a,b"`, `""` escapes).
  Use `--quote` for single quotes and other quote characters
- Only the cells under check are decoded as UTF-8, so unrelated columns may
  contain non-UTF-8 text such as CP932 (Shift_JIS) — the checked columns are
  digits and therefore ASCII. ASCII-incompatible encodings such as UTF-16 are
  not supported

Limits: values must fit in i128 (multi-column combinations up to 16 bytes
total, effectively 127 bits because the top bit is the sign). Double notation
is accepted only for integer values up to 2^53.

## Build and test

```console
$ cargo build --release      # target/release/hanuke.exe
$ cargo test
```

## License

MIT OR Apache-2.0 ([LICENSE-MIT](LICENSE-MIT) / [LICENSE-APACHE](LICENSE-APACHE))
