Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
56 lines
1.6 KiB
PHP
56 lines
1.6 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace LIT\MCP\Integrations\Divi\Release;
|
||
|
||
use LIT\MCP\Abilities\ToolRefusal;
|
||
use LogicException;
|
||
use RuntimeException;
|
||
use Throwable;
|
||
use WP_Error;
|
||
|
||
defined('ABSPATH') || exit;
|
||
|
||
/**
|
||
* A Divi call refused because a feature it needs is not released (spec §5.5, D6); AbilityRegistrar returns
|
||
* toError().
|
||
*/
|
||
final class DiviLocked extends RuntimeException implements ToolRefusal
|
||
{
|
||
/**
|
||
* @param non-empty-list<DiviFeature> $features The locked features the refused call needs.
|
||
* @throws LogicException When $features is empty – a lock always names what it locks.
|
||
*/
|
||
public function __construct(public readonly ReleaseStatus $status, public readonly array $features)
|
||
{
|
||
if ($features === []) {
|
||
throw new LogicException('a Divi lock needs the locked features');
|
||
}
|
||
|
||
parent::__construct(sprintf(
|
||
'%s is %s for LIT MCP – %s refused',
|
||
$status->versionLabel(),
|
||
$status->state->value,
|
||
implode(', ', array_map(static fn (DiviFeature $feature): string => $feature->value, $features))
|
||
));
|
||
}
|
||
|
||
public function toError(): WP_Error
|
||
{
|
||
return $this->status->refusalFor($this->features);
|
||
}
|
||
|
||
/** The lock anywhere in $e's chain of previous exceptions (callers wrap failures in WriteFailed). */
|
||
public static function in(Throwable $e): ?self
|
||
{
|
||
for ($cause = $e; $cause !== null; $cause = $cause->getPrevious()) {
|
||
if ($cause instanceof self) {
|
||
return $cause;
|
||
}
|
||
}
|
||
|
||
return null;
|
||
}
|
||
}
|