Skip to content

v1.0.0 — Stable API

Latest

Choose a tag to compare

@jamesgober jamesgober released this 20 Jun 17:26

span-lang v1.0.0 — Stable

The API freeze. v1.0.0 stabilises the surface built across the 0.x series and
puts it under a SemVer promise: no public item is removed or changed incompatibly
before 2.0.0. There are no code changes from 0.4.0 — the position types, the
span arithmetic, the UTF-8-correct resolver, the line index, and Spanned<T> are
exactly as shipped, now declared final and verified green on all three platforms.

What is span-lang?

The source-position substrate for language tooling. It defines the small,
copyable coordinate types that a lexer, parser, and diagnostic renderer all
share: a byte position, a byte-offset span, a resolved line/column, and the index
that maps between them — correctly over UTF-8, across \n and \r\n. It owns
positions and nothing else: it does not load source and does not render
diagnostics, which is what lets every layer above depend on it without inheriting
I/O or formatting.

The stable surface

Five public types, every item documented with a runnable example:

  • BytePos — a 4-byte Copy byte offset; new, to_u32, to_usize, u32
    conversions, Display.
  • Span — a packed half-open start..end byte range; new (orders its
    arguments to uphold start <= end), empty, start, end, len, is_empty,
    contains, an associative and commutative merge, total ordering, Display.
  • LineCol — a 1-based line/column whose column counts Unicode scalar values;
    new, line:col Display.
  • LineIndex — built once per source; line_col (byte → coordinate, total,
    O(log lines)), offset (the checked inverse), line_span (a line's text
    range, terminator trimmed), line_count.
  • Spanned<T> — a value paired with its span; new, map, as_ref,
    value @ start..end Display.

An optional serde feature serialises every position type, with Span
deserialisation routed through its constructor so the start <= end invariant
holds for untrusted input.

The SemVer promise

In force from 1.0.0 and recorded in
docs/API.md:

  • No public item is removed or changed incompatibly before 2.0.0. Additions, if
    any, are new items only.
  • The serde wire representations of BytePos, Span, LineCol, and
    Spanned<T> are part of the contract and will not change incompatibly within a
    major version.
  • The Display formats — 123, 4..10, 2:5, value @ 4..10 — are stable.
  • MSRV is 1.85; an MSRV increase is treated as a minor change.

Correctness and performance, verified

The section-4 invariants are held by property tests cross-checked against a naive
reference resolver over UTF-8 input including multi-byte characters and CRLF:
Span::new orders its arguments; merge is commutative, associative, and exact;
forward resolution matches a full character scan on every offset; byte ↔ (line,
col) round-trips for every valid position; and line spans tile the source without
ever containing a terminator.

Performance is backed by criterion, with the O(log lines) lookup demonstrated
by scaling, not asserted. Representative means:

Operation Windows x86_64 Linux x86_64 (WSL2)
Span::merge ~0.57 ns ~0.57 ns
LineIndex::offset ~2.5 ns ~2.0 ns
LineIndex::line_col ~8.7 ns ~8.8 ns
LineIndex::line_col @ 100 000 lines ~22 ns ~19 ns

A 1 000-fold increase in line count costs roughly 2.6× the lookup time —
logarithmic, as the binary search guarantees by construction.

Breaking changes

None. v1.0.0 is byte-for-byte the 0.4.0 surface. Code written against
0.4.0 compiles and behaves identically; the only change is the stability
guarantee.

Verification

Verified green on Windows x86_64 (Rust stable 1.95.x, MSRV 1.85) and Linux x86_64
(WSL2 Ubuntu); macOS x86_64/ARM64 via the CI matrix:

cargo fmt --all -- --check
cargo clippy --all-targets -- -D warnings
cargo clippy --all-targets --all-features -- -D warnings
cargo clippy --no-default-features --all-targets -- -D warnings
cargo test
cargo test --all-features
cargo build --no-default-features
cargo build --no-default-features --features serde
RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --all-features
cargo +1.85 build --all-features
cargo bench --bench bench
cargo audit
cargo deny check

All green on both platforms. Counts at this tag:

  • Default features: 32 unit + 7 property tests + 26 doctests.
  • --all-features: 32 unit + 7 property tests + 7 serde tests + 26 doctests.

What's next

The surface is frozen. Future releases are documentation, additional tests, and
internal optimisation under the SemVer promise — no public API change before a
hypothetical 2.0.0.

Installation

[dependencies]
span-lang = "1"

# With serialisation:
span-lang = { version = "1", features = ["serde"] }

MSRV: Rust 1.85.

Documentation


Full diff: v0.4.0...v1.0.0.
Changelog: CHANGELOG.md.