lino-objects-codec 0.9.1

A library to encode/decode objects to/from links notation
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.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [0.9.1] - 2026-10-11

### Changed
- Raised the `serde_json` floor to 1.0.154 (and refreshed `Cargo.lock`) so the Dependency Freshness gate passes again.
- CI: Dependabot pull requests are exempt from the changelog fragment check (decided from the PR author, not the branch name), so grouped dependency updates no longer fail it and get merged red.

## [0.9.0] - 2026-10-11

### Fixed

- Scan large readable documents directly in UTF-8 instead of materializing every character; retain exact Unicode and legacy quote behavior.

### Changed

- Refresh parser and test dependencies required by freshness CI.
- Require Rust 1.87 for the parser dependency, and 1.88 for development tests.

## [0.8.0] - 2026-10-07

### Added

- Public single-line and verbatim value formatting helpers, and an optional lossless `serde_json::Value` bridge preserving key order, number text and empty values.

### Changed

- Update links-notation to 0.23.0 and refresh the Cargo lockfile. CI tests the JSON feature and rejects stale dependencies.

### Fixed

- Disambiguate a readable array containing only null from the compact null document.

## [0.7.0] - 2026-08-28

### Changed
- Updated `links-notation` from 0.14.0 to 0.16.1. All four implementations now pin `links-notation` 0.16.1, so the conformance suites run against one grammar instead of four (issue #47).

### Changed
- Updated `base64` from 0.22 to 0.23.1 and the `serde_json` dev-dependency from 1.0.149 to 1.0.151.
- Moved the crate to edition 2024 and raised `rust-version` from 1.70 to 1.85. The declared 1.70 was already unreachable: `base64` 0.23 and `serde_json` 1.0.151 both require 1.71 or newer, so the floor had to move regardless (issue #47).

## [0.6.0] - 2026-08-27

### Changed

- `encode` and `encode_line` never reach for base64. A single control character
  used to turn a whole string into base64, so a log message holding one newline
  hid its own text: the message, the stack trace and every word a reader would
  grep for. Both readable forms now write the text as it is and escape only what
  the form itself cannot carry — the newline on a single line, the carriage
  return everywhere, and the remaining control characters — in a value marked
  `(escaped "line one%0Aline two")` whose payload is percent-escaped, so even the
  escaped part stays readable. base64 is reachable only through
  `encode_compact()` / `encode_obfuscated()`, which say so by name. See
  [issue #45]https://github.com/link-foundation/lino-objects-codec/issues/45.
- A string containing the quote delimiter is written between a run of at least
  three of them — `"""say "hi""""` — instead of by doubling the quote. The
  doubled form desynchronises `links-notation`'s own parser; the run form reads
  back unchanged, which `tests/plain_text_values.rs` checks with
  `parse_lino_to_links`.

### Added

- `readable::ESCAPED_MARKER`, the `escaped` link id that marks a
  percent-escaped value.

### Fixed

- A key holding a control character stays a key. It used to be written as
  `(base64 "…")` in key position and read back as an array element, so
  `{"a\nb": "a\nb"}` decoded to `["a\nb", "a\nb"]`.
- `(base64 "…")` is still decoded, so every document written up to 0.5.0 keeps
  reading; the shared fixtures pin this in a `legacy` section.

## [0.5.0] - 2026-08-27

### Added

- `encode_line` and `decode_line`: the readable format written on one line, so
  an append-only log holds one record per line. Appending is one write,
  compaction cuts at a newline, and `grep`, `tail -f` and `wc -l` treat a line
  as one event. The output is valid Links Notation, keeps numbers, booleans and
  `null` bare so types survive the round trip, and `decode(encode_line(v))`
  equals `decode(encode(v))`. See
  [issue #43]https://github.com/link-foundation/lino-objects-codec/issues/43.
- `readable::OBJECT_MARKER`, the `o` link id that tells an object from an array
  on one line: `(o: (bytes 2827) (complete true))` is a record, `("a" 1)` is a
  two-element array, `(o:)` is the empty object and `()` the empty array. The
  marker is part of the notation, so the empty key round-trips as `(o: ("" 2))`.
- The `append_only_log` example, which writes, counts, greps and reads back a
  log of one record per line.

### Fixed

- `decode` no longer routes a readable single-line document such as `(null 1)`
  to the compact (base64) reader. Only a compact document keeps that path; the
  document `(null)` stays the compact null so older documents keep decoding.

## [0.4.1] - 2026-08-20

### Changed

- CI/CD hardening from
  [issue #41]https://github.com/link-foundation/lino-objects-codec/issues/41:
  every action moves to a Node 24 major, each job gets a `timeout-minutes` and
  its own concurrency group, release jobs queue instead of racing so a
  `cargo publish` can no longer be cancelled mid-flight, and pull requests are
  now validated against a fresh merge with the base branch rather than a stale
  merge preview. No change to the crate's code or public API.

### Fixed

- The change-detection script compared repository-root paths
  (`rust/examples/demo.rs`) against package-relative prefixes (`examples/`), so
  the documented exclusions never applied and `toml-changed` fired on any
  `.toml` in the repository, including `python/pyproject.toml`.

## [0.4.0] - 2026-08-20

### Added

- Opt-in tracing via the `LINO_CODEC_DEBUG` environment variable (`1`, `true`,
  `yes`, `on`) or `debug::set_debug_enabled` from code, matching the JavaScript,
  Python and C# implementations. See
  [issue #39]https://github.com/link-foundation/lino-objects-codec/issues/39.

### Changed

- The readable indented output is now verified byte-for-byte against the other
  three languages through the shared fixtures in
  `fixtures/readable-format/cases.json`.

### Fixed

- Cross-language compact interop: booleans in the compact form are now decoded
  case-insensitively, so `(bool True)` written by the Python or C#
  implementations decodes correctly in Rust.

## [0.3.0] - 2026-08-20

### Added
- `encode_with_indent()` for choosing the indentation string of the readable format.
- `encode_compact()` (alias `encode_obfuscated()`) keeping the previous single-line base64 output under an explicit name.
- `readable` module with the indented encoder/decoder, plus `DEFAULT_INDENT` and `BASE64_MARKER` constants.

### Changed
- `encode()` now produces indented, plain-text Links Notation by default: one `( )` construct for objects and arrays at every level, keys and values written verbatim, strings double-quoted, and numbers/`true`/`false`/`null` bare so types survive a round trip.
- Values are base64-encoded only when they cannot be written as text (control characters), and each such value is marked individually as `(base64 "...")`.
- `decode()` accepts both the readable and the previous compact form, so existing files keep working and migrate to the readable form on the next write.
- Raised the `links-notation` dependency to 0.14, where parentheses open a nested indentation context.

## [0.2.1] - 2026-05-03

Add CI/CD improvements based on template best practices:

- Add detect-code-changes.mjs for smart change detection
- Add check-version-modification.mjs to prevent manual Cargo.toml version changes
- Add check-changelog-fragment.mjs for PR-diff-based changelog fragment checking
- Add check-file-size.mjs for Rust file line limit checking
- Add git-config.mjs for CI git configuration
- Update workflow with detect-changes job, version check, file size check,
  and improved concurrency configuration

Harden the Rust release workflow so ambiguous crates.io responses fail instead of triggering a duplicate publish, and test the release-decision logic.

## [0.1.0] - 2024-12-17

### Added
- Initial release of lino-objects-codec for Rust
- `LinoValue` enum for representing all serializable types
- `encode()` and `decode()` convenience functions
- `ObjectCodec` struct for direct usage
- Support for all basic types: null, bool, int, float, string
- Support for special float values: NaN, Infinity, -Infinity
- Support for collections: array, object
- Circular reference detection and handling
- UTF-8 string support via base64 encoding
- Comprehensive error handling with `CodecError`
- Example usage in `examples/basic_usage.rs`
- Full documentation with doc tests