Skip to content

feat!: API foundation (errors, options, warnings, non_exhaustive) - #26

Merged
Nathan-D-R merged 8 commits into
mainfrom
fix/api-foundation
Oct 25, 2026
Merged

Nathan-D-R merged 8 commits into
mainfrom
fix/api-foundation

Conversation

@Nathan-D-R

@Nathan-D-R Nathan-D-R commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

Description

API foundation for the final major release. It reshapes the public API so the silent-failure fixes stacked on this PR, and later fixes, can land without another break. Decode behaviour is unchanged here; the fix PRs change it.

What it adds:

  • Error is #[non_exhaustive] with Error::kind() -> ErrorKind (Io, Format, Usage, Integrity, Unsupported, Keystream, Structure) and Error::page(). It also carries the variants the fix PRs report. Struct-like variants are #[non_exhaustive] too, so a field can be added without breaking a match.
  • ReadOptions (strict by default) holding one extensible Leniency set (KEYSTREAM, DIRECTORY, INTEGRITY, FORMAT). ReadOptions::lenient() relaxes exactly those four flags, and a flag added later is not added to it. ReadOptions::widest() and Leniency::every_known() widen with each release; their docs say so.
  • Warning / WarningKind: a typed variant for every condition the crate reports. WarningKind::Other(code) is for dependent crates, and Warning::new, on_page and on_sector are public so OpenQBW can add its own.
  • Result types instead of bare values: DeobfuscatedPage, LearnReport, Row (with an exact flag).
  • Private fields with accessors on PageTrailer, Superblock, SlotDirectory, SlottedPage; PageTrailer::raw() is the offset-neutral escape hatch.
  • #[non_exhaustive] on every public enum and data struct. No Python bindings live in this repo, so there is no exception hierarchy to add here.

Review follow-up

Review finding S6 (all four parts) and nits N3 (caller errors get ErrorKind::Usage) and N4 (README and lib docs) are fixed in separate commits on top of the original two. Each API break is its own commit: struct-variant non_exhaustive, typed WarningKind plus public constructors, and the lenient() / widest() split.

API breaks

Each is listed in the CHANGELOG under Unreleased.

  • ApModel::learn(&store, &ReadOptions) -> Result<ApModel>; deobfuscate_with_store becomes ApModel::deobfuscate(&store, pn, &opts) -> Result<DeobfuscatedPage>; deobfuscate_with_bv becomes an associated function over a Page.
  • Removed: ApModel::bv_at, block-level deobfuscate, recover_bv_for_block, recover_bv_for_page, Default for ApModel (they guess a key silently), and SlottedPage::overflow_prefix (returns the page prelude as record data, silent-failure finding 9).
  • SlottedPage::parse(page, &opts) -> Result<_>; row_bytes() becomes rows() -> Vec<Row>; .directory and .page become accessors.
  • PageStore::pages() yields Result<Page> (finding 18).
  • Page::from_bytes and Superblock::parse return Result and reject any wrong length instead of panicking or truncating.
  • PageTrailer / Superblock / SlotDirectory public fields become accessors; SlotDirectory::scan_start and leading_zero are dropped from the public surface.
  • PageType::UnknownG removed ('G' is Other(b'G')); PageType is #[non_exhaustive]. PageTrailer::case_variant() is added instead of putting the case bit in the enum, so == PageType::Extent keeps working.
  • Struct-like variants of Error and WarningKind are #[non_exhaustive]: match them with ...
  • Leniency::all() is renamed Leniency::every_known(); ReadOptions::lenient() is a fixed set; ReadOptions::widest() is new.

Tests changed

  • crates/opensqlany/src/ap.rs: deobfuscate_with_store_uses_per_page_bv_not_block_bv becomes deobfuscate_uses_per_page_bv_not_block_bv (same assertion on the new API).
  • crates/opensqlany/tests/synthetic.rs: ported to accessors, Result items and .. in struct-variant patterns. Added tests for error kinds, case_variant, wrong-length buffers, Leniency.

Docs-site edits needed (for the release PR; not edited here)

  • docs/docs/ap-deobfuscation.md: new learn / deobfuscate signatures, DeobfuscatedPage, ReadOptions.
  • docs/docs/quickstart-rust.md: sb.format(), pages() items are Result, accessor names.
  • docs/docs/openqbw.md: the architecture description.

OpenQBW follow-up

OpenQBW (opensqlany = "0.2.0") needs to adopt the new signatures when it bumps: ApModel::learn(&store, &opts)? at about 14 CLI sites and openqbw-py/src/lib.rs; deobfuscate_with_store becomes deobfuscate(&store, pn, &opts)? (attribution_schema.rs, attribution_content.rs, systable.rs, sysindex.rs, sysobject.rs); SlottedPage::parse(page, &opts)?, .directory(), .rows() in attribution_schema.rs, attribution_content.rs, transaction_header.rs. PageType::Extent comparisons are unchanged. Use ReadOptions::lenient() or name the Leniency flags explicitly; do not rely on widest().

Checklist

  • My commits are signed off (DCO): git commit -s
  • I have read CONTRIBUTING.md, including the vendor software and clean-room policy
  • This PR was not written or verified with the help of proprietary vendor software

…ult types

Every decode entry point returns Result and takes ReadOptions. Errors
are non_exhaustive with ErrorKind categories. Leniency is one extensible
set. Warnings carry accepted conditions on results. Trailer, superblock
and slot directory fields become accessors. PageType drops UnknownG and
gains PageTrailer::case_variant. Pages yields Result<Page>. The block
level and guessing calibration helpers and overflow_prefix are removed.
Decode behaviour is unchanged by this commit.

Signed-off-by: Nathan Riley <git@nathanriley.com>
Signed-off-by: Nathan Riley <git@nathanriley.com>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
opensqlanywhere-docs 2237c92 Oct 25 2026, 07:44 AM

A field can be added to a variant such as SlotDirectoryTruncated or
LowConfidenceSector without breaking callers that match it with ..

Signed-off-by: Nathan Riley <git@nathanriley.com>
Warning::new, on_page and on_sector become public so dependent crates
can add warnings of their own. WarningKind::Other is reserved for them.

Signed-off-by: Nathan Riley <git@nathanriley.com>
Signed-off-by: Nathan Riley <git@nathanriley.com>
lenient() relaxes exactly KEYSTREAM, DIRECTORY, INTEGRITY and FORMAT, so
a flag added later never widens it. Leniency::all() becomes
Leniency::every_known() and ReadOptions::widest() is the constructor
whose documentation says it widens.

Signed-off-by: Nathan Riley <git@nathanriley.com>
…Kind::Usage

WrongBufferLength and PageOutOfRange are caller errors, not format
errors.

Signed-off-by: Nathan Riley <git@nathanriley.com>
Signed-off-by: Nathan Riley <git@nathanriley.com>
@Nathan-D-R
Nathan-D-R merged commit 4b4677b into main Oct 25, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant