Skip to content

feat: structured exception hierarchy#32

Merged
ptondereau merged 1 commit into
mainfrom
feat/structured-exceptions
May 26, 2026
Merged

feat: structured exception hierarchy#32
ptondereau merged 1 commit into
mainfrom
feat/structured-exceptions

Conversation

@ptondereau
Copy link
Copy Markdown
Owner

@ptondereau ptondereau commented May 11, 2026

Address #14

Replaces the flat Invalid* classes with a deep typed hierarchy rooted at Biscuit\Exception\BiscuitException. Every failure shape has its own concrete subclass, so callers use PHP multi-catch instead of branching on a message string or an int code. AuthorizationException and every DatalogException subclass carry structured payloads (matched policy + failed checks; parse errors + missing/unused parameters).

Hierarchy

graph TD
    Ex["\Exception"] --> Biscuit[BiscuitException]
    Biscuit --> Key[KeyException]
    Biscuit --> Datalog[DatalogException]
    Biscuit --> Format[FormatException]
    Biscuit --> Build[BuildException]
    Biscuit --> Authz[AuthorizationException]
    Biscuit --> Third[ThirdPartyException]
    Biscuit --> Builder[BuilderStateException]

    Key --> PublicKeyException
    Key --> PrivateKeyException

    Datalog --> FactException
    Datalog --> RuleException
    Datalog --> CheckException
    Datalog --> PolicyException
    Datalog --> TermException
    Datalog --> ScopeException

    Format --> Base64Exception
    Format --> BytesException
    Format --> SignatureException
    Format --> SnapshotException

    Build --> BiscuitBuildException
    Build --> BlockAppendException
    Build --> AuthorizerBuildException
    Build --> ThirdPartyBlockAppendException
Loading

All exceptions live in Biscuit\Exception\. Catch the leaf for precise handling or the parent (KeyException, DatalogException, FormatException, BuildException, or BiscuitException ) to catch a category.

Breaking change. Full migration guide in UPGRADING.md.

@ptondereau ptondereau force-pushed the feat/structured-exceptions branch from 3a06fce to f3fadc2 Compare May 11, 2026 18:09
@ptondereau ptondereau changed the title feat: structured exception hierarchy (v0.5.0) feat: structured exception hierarchy May 11, 2026
@ptondereau ptondereau force-pushed the feat/structured-exceptions branch from f3fadc2 to 4d073f7 Compare May 11, 2026 18:14
@ptondereau ptondereau marked this pull request as ready for review May 11, 2026 18:17
@ptondereau
Copy link
Copy Markdown
Owner Author

@shulard FYI, it should land for v0.5.0, it will ease the error hierarchy for production/dev monitoring

@shulard
Copy link
Copy Markdown

shulard commented May 12, 2026

Really nice work thank you for the ping !

@ptondereau ptondereau force-pushed the feat/structured-exceptions branch from 4d073f7 to 5cdc490 Compare May 13, 2026 16:51
Replace the flat `Invalid*` exception list with a deep typed hierarchy
rooted at `Biscuit\Exception\BiscuitException`. Every failure shape now
has its own concrete subclass so callers can use PHP's idiomatic
multi-catch instead of branching on a message string or an int code.

Hierarchy:
- BiscuitException (base, extends \Exception)
  - KeyException -> PublicKeyException, PrivateKeyException
  - DatalogException -> FactException, RuleException, CheckException,
    PolicyException, TermException, ScopeException
  - FormatException -> Base64Exception, BytesException,
    SignatureException, SnapshotException
  - BuildException -> BiscuitBuildException, BlockAppendException,
    AuthorizerBuildException, ThirdPartyBlockAppendException
  - AuthorizationException, ThirdPartyException, BuilderStateException

Structured payloads:
- AuthorizationException carries getMatchedPolicy() and
  getFailedChecks() with concrete MatchedPolicy and FailedCheck value
  objects.
- Every DatalogException subclass carries getParseErrors(),
  getMissingParameters(), getUnusedParameters() populated from the
  upstream LanguageError.

Rust side:
- BiscuitError enum + ResultExt trait centralise error tagging
  through .key() / .datalog() / .format() / .build() / .third_party()
  on any Result.
- Two macro_rules generators (marker_subclass! and datalog_subclass!)
  keep the 16 subclass declarations DRY despite ext-php-rs not
  auto-propagating methods between Rust-defined parent/child classes.
- From<BiscuitError> for PhpException dispatches each kind variant to
  its concrete subclass.

Includes regenerated stubs, full UPGRADING.md migration guide with
per-class catch examples, and updated PHPUnit coverage (116 tests).

Closes #14
@ptondereau ptondereau force-pushed the feat/structured-exceptions branch from 5cdc490 to 2dac331 Compare May 13, 2026 16:52
@ptondereau ptondereau merged commit 6576d8b into main May 26, 2026
8 checks passed
@ptondereau ptondereau deleted the feat/structured-exceptions branch May 26, 2026 15:29
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.

2 participants