sheeter-engine 0.7.0

A CLI tool for converting data files between formats. v0.1.0 supports CSV <-> JSON.
Documentation
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [0.7.0] - 2026-11-02

### Added

- XLSX support via `calamine` (reader) and `rust_xlsxwriter` (writer), both
  pure Rust with no C dependencies.
- `Format::Xlsx`, `CanonicalValue::from_xlsx_workbook`,
  `CanonicalValue::to_xlsx_workbook`.
- `convert_workbook(from, to, bytes)` returns one `(sheet_name, content)`
  pair per output document, enabling multi-sheet workbooks.
- CLI `--sheet NAME` flag to extract a single sheet by name.
- Multi-sheet XLSX to CSV/TSV produces one file per sheet, using the pattern
  `<stem>.<SheetName>.<ext>`. Collisions after sanitization get `_2`, `_3`
  suffixes.

### Changed

- `convert(from, to, &str)` now rejects `Format::Xlsx` with a pointer to
  `convert_workbook`; XLSX is binary and cannot round-trip through `&str`.
- JSON/YAML/XML/TOML outputs from multi-sheet XLSX emit a single document
  whose root is an object keyed by sheet name.
- `Format::ALL` now has 7 entries.

### Notes

- Dates are stringified (ISO-8601), matching TOML and YAML behavior.
- Formulas are read as their cached computed values, if present. The Sheeter
  never evaluates formulas.
- Formatting, merged cells, charts, images, and pivot tables are ignored.
- Writing is `.xlsx` only. Reading `.xls` and `.ods` via `calamine` is not
  wired to the CLI in this release.

## [0.6.0] - 2026-11-02

### Changed

- CLI collapsed from 30 pairwise subcommands to a single `convert`
  subcommand with `--from` and `--to` flags. Usage becomes
  `sheeter convert --from <FMT> --to <FMT> <input> <output>`.
- New `Format` enum and `convert(from, to, input)` function in the library
  expose the hub directly; the 30 pairwise functions remain available.

### Added

- Property-based tests with `proptest` for `csv → json → csv` and
  `tsv → json → tsv` round-trip byte equality.

### BREAKING CHANGE

- CLI: `sheeter csv-to-json` and the other 29 pairwise subcommands were
  replaced by `sheeter convert --from csv --to json`. Scripts and CI
  invoking the old syntax must be updated.

## [0.5.0] - 2026-11-02

### Added

- TOML support via the `toml` crate (pure Rust): `toml_to_json`,
  `json_to_toml`.
- Cross-format TOML wrappers: `toml_to_csv`, `toml_to_tsv`, `toml_to_yaml`,
  `toml_to_xml`, `csv_to_toml`, `tsv_to_toml`, `yaml_to_toml`, `xml_to_toml`.
- CLI subcommands for all 10 new routes.
- TOML tabular data is discovered via the first array-of-tables found at any
  depth, so nested configurations produce CSV/TSV without manual unwrapping.

### Changed

- `as_tabular` now accepts flat objects (all scalar values) as a single row,
  enabling `toml_to_csv` on simple documents and making `<r>just text</r>`
  produce `r\njust text\n` instead of erroring.

### Notes

- TOML datetimes are stringified, matching YAML behavior. Round-trip back to
  TOML restores them as strings, not as date types.
- `null` values in JSON are skipped during JSON→TOML conversion (TOML has no
  null type). JSON arrays containing null error out.
- JSON arrays at root are wrapped in `{item: [...]}` on TOML output, matching
  the XML wrapper convention.

### BREAKING CHANGE

- `as_tabular` behavior: XML with a scalar root (`<r>just text</r>`) now
  produces a single-row CSV output instead of erroring. Consumers that
  relied on the previous error should check for this input shape explicitly.

## [0.4.0] - 2026-11-02

### Added

- `CanonicalValue` type: format-agnostic intermediate representation that
  replaces the JSON string hub. Every reader produces a `CanonicalValue`;
  every writer consumes one.
- XML support via `quick-xml` (pure Rust, streaming, no C dependencies):
  `xml_to_json`, `json_to_xml`.
- Cross-format XML wrappers: `csv_to_xml`, `tsv_to_xml`, `yaml_to_xml`,
  `xml_to_csv`, `xml_to_tsv`, `xml_to_yaml`.
- CLI subcommands for every new conversion route.

### Changed

- The hub is no longer a serialized JSON string. Conversions between formats
  that are not JSON compose through `CanonicalValue`, so adding a new format
  now costs one reader and one writer, not `N-1` pairwise converters.

### Notes

- XML documents are represented as a single-key object whose key is the root
  element name. Attributes use `@name` keys; text uses `#text`. Repeated
  sibling elements become arrays. `xml_to_csv` and `xml_to_tsv` extract the
  tabular array when the XML follows `<root><item>...</item></root>`.

## [0.3.1] - 2026-11-02

### Fixed

- `yaml_to_json` now treats `!!binary`-tagged scalars as literal strings instead
  of silently base64-decoding them, preventing value corruption for annotated
  YAML documents.

### Added

- Regression tests for ragged-row rejection in CSV/TSV and `!!binary`
  string preservation.
- Documented that non-string YAML keys are stringified, matching mainstream
  YAML-to-JSON converters.

## [0.3.0] - 2026-11-02

### Added

- YAML output for all tabular sources: `csv_to_yaml`, `tsv_to_yaml`, `json_to_yaml`.
- YAML input for all tabular targets: `yaml_to_csv`, `yaml_to_tsv`, `yaml_to_json`.
- Direct `csv_to_tsv` and `tsv_to_csv` conversions.
- CLI subcommands for every new route.
- YAML support uses `serde-saphyr` (pure Rust, YAML 1.2, no C dependency).

### Fixed

- `csv_to_json` / `tsv_to_json` now strip UTF-8 BOM before parsing, so
  Excel-exported files no longer produce headers like `\u{feff}name`.
- `yaml_to_json` uses `strict_booleans` to avoid the YAML 1.1 "Norway problem":
  `yes`/`no`/`on`/`off` are kept as strings, matching YAML 1.2 semantics.
- `json_to_yaml` and `yaml_to_json` preserve `null` values instead of
  stringifying them.
- `json_to_csv` / `json_to_tsv` build the header from the union of all object
  keys in first-appearance order, guaranteeing stable round-trips regardless
  of per-object key ordering.
- `json_to_tsv` correctly quotes cells containing tab characters, preventing
  line-splitting corruption.
- Dependencies pinned to `serde-saphyr` `0.0.29` (pure Rust, YAML 1.2, no C
  dependency) to keep the crate portable to Android/Termux.

## [0.2.0] - 2026-11-02

### Added

- `tsv_to_json` and `json_to_tsv` library functions.
- `tsv-to-json` and `json-to-tsv` CLI subcommands.
- TSV support reuses the same lossless conversion engine as CSV, with
  identical duplicate-header and nested-value rejection rules.

## [0.1.2] - 2026-11-02

### Fixed

- `csv_to_json` now errors on duplicate CSV headers instead of silently
  dropping columns.
- `json_to_csv` now collects the union of keys across all objects instead
  of using only the first object's keys, which silently dropped data.
- `json_to_csv` now errors on nested JSON objects/arrays instead of
  stringifying them and corrupting round-trips.

## [0.1.1] - 2026-11-02

### Changed

- Rename crate from `sheeter` to `sheeter-engine` to avoid collision with an
  existing crate on crates.io. Import path changes from `sheeter::` to
  `sheeter_engine::`.

## [0.1.0] - 2026-11-02

### Added

- Initial release of Sheeter CLI.
- CSV to JSON conversion.
- JSON to CSV conversion.
- Support for reading from and writing to files.