Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Rusty Laravel Package

kphoen/rusty

Rusty brings Rust-inspired Result and Option types to PHP and Laravel, enabling explicit error handling and nullable values without exceptions or scattered null checks. Fluent helpers make safer, more expressive code easy to write.

View on GitHub
Deep Wiki
Context7

Getting Started

Rusty brings Rust-style doctests to PHP: fenced code blocks inside docblocks are extracted, turned into executable tests, and run alongside (or instead of) your normal suite. The pitch is simple — if the example in your docs stops working, CI goes red.

composer require --dev kphoen/rusty

Where to look first, in order:

  1. The README's Usage section — this is a 0.x, archived package, so the exact entry point (a PHPUnit TestCase/TestSuite subclass vs. a standalone binary) is the one thing you must confirm before wiring anything.
  2. The package's own tests/ directory — it doubles as the spec for which markdown constructs are supported (fenced ```php blocks vs. indented blocks, how the expected output/exception is expressed).
  3. The parser class in src/ — when an example doesn't do what you expect, the parser is what you'll be reading.

Most projects wire it as a dedicated PHPUnit test case pointed at a directory of source files:

// tests/DocumentationTest.php
use KPhoen\Rusty\PHPUnit\DocTestCase; // exact name per README

final class DocumentationTest extends DocTestCase
{
    protected function paths(): array
    {
        return [__DIR__ . '/../src'];
    }
}

First use case: pick one small, stable value object or factory and document it.

/**
 * Create a money value from an integer number of cents.
 *
 * ```php
 * $money = Money::fromCents(1500, 'EUR');
 * assert($money->getAmount() === 1500);
 * assert((string) $money === '15.00 EUR');
 * ```
 */
public static function fromCents(int $amount, string $currency): self

Run it, watch it pass, then deliberately break the implementation and confirm the doc suite fails. If it doesn't fail, you have an assert() configuration problem, not a Rusty problem (see Gotchas).

Implementation Patterns

Docblocks as the contract, unit tests as the edge cases. Doc examples should read like usage documentation: the happy path plus the one surprising behaviour. Leave exhaustive matrix testing to PHPUnit. A good ratio is one code block per public method, 3–8 lines each.

Self-contained blocks. Treat every block as an isolated script. Rebuild fixtures inside the block rather than relying on state from a previous example or from the test file — ordering and shared static state are not guarantees you want to depend on.

Namespace inheritance. Examples are typically executed in the namespace of the file they live in, so unqualified class names resolve. Do not assume use statements from the file carry over; if a block needs a foreign class, either fully qualify it or put the use inside the block. Confirm this against the parser, because it is the single most common source of "works in the file, explodes in the doc test".

Documenting failures explicitly. Prefer a visible try/catch over a magic "expected exception" marker:

/**
 * ```php
 * try {
 *     Money::fromCents(-1, 'EUR');
 *     throw new \LogicException('should have thrown');
 * } catch (\InvalidArgumentException $e) {
 *     assert($e->getMessage() === 'Amount must be positive');
 * }
 * ```
 */

It reads better in rendered docs and doesn't depend on harness-specific annotation support.

Namespace the suite in CI. Give it its own <testsuite> so you can run --testsuite documentation in seconds on every PR, and keep it out of the slow integration job. A red doc suite means stale README — that's the whole value proposition, so make it fast and visible.

Layer with static analysis deliberately. PHPStan/Psalm parse docblocks for @param/@return but will not type-check code fences by default; you're getting runtime verification only. That's fine — just don't assume the examples are also statically verified.

Migration strategy. Start with the public API surface of one or two namespaces. Retrofitting every class is a large diff with low payoff, and this package will not be getting fixes, so keep the blast radius small enough that you could delete the doc suite in an afternoon.

Gotchas and Tips

  • assert() silently does nothing. In any production-ish php.ini, zend.assertions=-1 means every example "passes" without running its checks. zend.assertions cannot be changed at runtime with ini_set() for the -1 case — set it in CI explicitly: php -d zend.assertions=1 -d assert.exception=1 vendor/bin/phpunit. This is the number-one way teams adopt Rusty and get zero value.
  • Archived, 0 dependents, 47 stars. Assume no PHP 8.2+/PHPUnit 10+ compatibility and no security response. Pin the version, and if you adopt it seriously, vendor or fork it — the parser and the test-case glue are small enough to own.
  • PHPUnit major-version coupling. The harness extends PHPUnit's TestCase/TestSuite; a PHPUnit upgrade will likely break it before your own tests break. Forking is the mitigation.
  • */ inside a code fence terminates the docblock. So does an unbalanced /*. Keep examples free of block-comment syntax, and be careful with regex literals and paths containing */.
  • Fence indentation. Every line of a docblock carries a leading *. Mixed tabs/spaces or a stray un-indented line can truncate or corrupt extraction. If a block misbehaves, the fix is almost always whitespace.
  • Static state leaks between examples. Singletons, static caches, and container bindings persist for the whole process. Either design examples that don't touch them, or reset explicitly at the top of the block.
  • Output comparison is fragile. If the harness compares captured output, PHP notices, deprecations, and echo/print ordering all become assertions. Suppress noise, or prefer inline assert() over output matching.
  • Autoloading. Examples execute outside your usual bootstrap context in some wirings; if you get "class not found" only in doc tests, register Composer's autoloader in the suite's setup rather than chasing the parser.
  • Debugging tip: temporarily dump the generated code string from the parser to a file before it executes. Reading the actual extracted snippet resolves ~90% of "why doesn't this run" questions faster than reading the docblock again.
  • Extension point: the code-block parser is the natural seam. Subclass it (or the suite) to add custom fencing languages, strip use lines, or inject a shared fixture setup — cheaper than fighting the defaults.
  • Don't let docs become a second test suite. The moment examples start covering every branch, they're slow, brittle, and duplicated. Keep them usage-shaped.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource