test-lang v1.0.0 — API Freeze
The public surface is now stable. Everything that shipped in 0.2.0 — the four types, their methods, their trait implementations — is frozen under SemVer: no breaking change lands before a 2.0, and 1.x releases add capability without removing or altering what exists. There are no functional changes from 0.2.0; this release records the promise.
What is test-lang?
A snapshot test harness for language front-ends. Give it source, run that source through a lexer, parser, or diagnostics renderer, and assert the rendered result against a known-good block of text. When the output changes, you get a line-level unified diff pointing at exactly what moved, and accepting the new behavior is a copy-paste. It owns no grammar and depends on no other front-end crate — it works over core::fmt::Display and Debug, so the same harness serves a hand-written lexer or a generated parser without coupling to either. no_std + alloc supported.
The frozen surface
Two types you construct, one you receive on failure, and the pair that describes what differed:
Snapshot— a normalized, comparable rendering of some stage's output.new,display,debug,per_line— capture from a string, aDisplayvalue, a pretty-printedDebugtree, or a sequence of items (one per line).as_str,check— read the normalized text; compare against expected text, returningResult<(), Mismatch>.- Implements
Display+ the standardDebug/Clone/PartialEq/Eq/Hash.
Diff+Change— the line-level edit script (LCS with a common prefix/suffix fast path), rendered as a unified-expected/+actualdiff.Diff::lines,is_empty,changes;Change::marker.
Mismatch— the error fromSnapshot::check, carrying theDiff, implementingDisplayandcore::error::Error.
use test_lang::Snapshot;
let snapshot = Snapshot::per_line(["let", "x", "=", "1"]);
snapshot.check("let\nx\n=\n1").expect("token stream matches");The full reference, with parameters and multiple runnable examples per item, is in docs/API.md — now marked stable.
What the freeze means
- No breaking changes before 2.0. Method signatures, type names, variant names, and trait impls listed in
docs/API.mdare fixed. - 1.x is additive. New constructors, helpers, or trait impls may arrive in minor releases; nothing already present will be removed or changed.
- Normalization is part of the contract. CRLF/CR collapse to LF, trailing whitespace is stripped, and trailing blank lines are trimmed — so a snapshot written on one platform matches output captured on another. This behavior is stable.
Breaking changes
None. The API is identical to 0.2.0. The only documentation correction: the std feature description now states accurately that the Error impl is core::error::Error in both std and no_std builds — the feature only controls whether the standard library is linked.
Verification
Re-ran the full gate on Windows x86_64 (Rust stable 1.95) and Linux (WSL2 Ubuntu); MSRV 1.85 verified locally; the CI matrix covers Linux / macOS / Windows × stable / 1.85:
cargo fmt --all -- --check
cargo clippy --all-targets -- -D warnings
cargo clippy --all-targets --all-features -- -D warnings
cargo test
cargo test --all-features
cargo test --no-default-features
cargo build --examples
cargo +1.85 build --all-features
RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --all-features
cargo deny checkAll green. Test counts at this tag: 30 unit + 8 integration + 6 property + 18 doctests.
What's next
The public API is frozen; future 1.x work is additive and driven by real usage — candidates include a by-value Display capture helper, a compact ({:?}) debug variant, and a convenience assertion macro. None are required, and none will break existing code.
Installation
[dependencies]
test-lang = "1"
# no_std + alloc
test-lang = { version = "1", default-features = false }MSRV: Rust 1.85.
Documentation
Full diff: v0.2.0...v1.0.0.
Changelog: CHANGELOG.md.