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-byteCopybyte offset;new,to_u32,to_usize,u32
conversions,Display.Span— a packed half-openstart..endbyte range;new(orders its
arguments to upholdstart <= end),empty,start,end,len,is_empty,
contains, an associative and commutativemerge, total ordering,Display.LineCol— a 1-based line/column whose column counts Unicode scalar values;
new,line:colDisplay.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..endDisplay.
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
serdewire representations ofBytePos,Span,LineCol, and
Spanned<T>are part of the contract and will not change incompatibly within a
major version. - The
Displayformats —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 checkAll 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.