mirror of
https://github.com/danog/Valinor.git
synced 2024-11-26 20:24:40 +01:00
feat: introduce utility class to build messages
The new class `\CuyZ\Valinor\Mapper\Tree\Message\MessageBuilder` can be used to easily create an instance of (error) message. This new straightforward way of creating messages leads to the depreciation of `\CuyZ\Valinor\Mapper\Tree\Message\ThrowableMessage`. ```php $message = MessageBuilder::newError('Some message / {some_parameter}.') ->withCode('some_code') ->withParameter('some_parameter', 'some_value') ->build(); ```
This commit is contained in:
parent
de8aa9f440
commit
cb87925aac
@ -37,7 +37,7 @@ Here is an implementation example for the [nesbot/carbon] library:
|
||||
// Carbon uses its own exceptions, so we need to wrap it for the mapper
|
||||
->filterExceptions(function (Throwable $exception) {
|
||||
if ($exception instanceof \Carbon\Exceptions\Exception) {
|
||||
return \CuyZ\Valinor\Mapper\Tree\Message\ThrowableMessage::from($exception);
|
||||
return \CuyZ\Valinor\Mapper\Tree\Message\MessageBuilder::from($exception);
|
||||
}
|
||||
|
||||
throw $exception;
|
||||
|
@ -110,21 +110,22 @@ try {
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Use provided message class
|
||||
### 2. Use provided message builder
|
||||
|
||||
The built-in class `\CuyZ\Valinor\Mapper\Tree\Message\ThrowableMessage` can be
|
||||
thrown.
|
||||
The utility class `\CuyZ\Valinor\Mapper\Tree\Message\MessageBuilder` can be used
|
||||
to build a message.
|
||||
|
||||
```php
|
||||
final class SomeClass
|
||||
{
|
||||
public function __construct(private string $value)
|
||||
{
|
||||
if ($this->value === 'foo') {
|
||||
throw \CuyZ\Valinor\Mapper\Tree\Message\ThrowableMessage::new(
|
||||
'Some custom error message.',
|
||||
'some_code'
|
||||
);
|
||||
if (str_starts_with($this->value, 'foo_')) {
|
||||
throw \CuyZ\Valinor\Mapper\Tree\Message\MessageBuilder::newError(
|
||||
'Some custom error message: {value}.'
|
||||
)
|
||||
->withCode('some_code')
|
||||
->withParameter('value', $this->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -161,7 +162,7 @@ try {
|
||||
(new \CuyZ\Valinor\MapperBuilder())
|
||||
->filterExceptions(function (Throwable $exception) {
|
||||
if ($exception instanceof \Webmozart\Assert\InvalidArgumentException) {
|
||||
return \CuyZ\Valinor\Mapper\Tree\Message\ThrowableMessage::from($exception);
|
||||
return \CuyZ\Valinor\Mapper\Tree\Message\MessageBuilder::from($exception);
|
||||
}
|
||||
|
||||
// If the exception should not be caught by this library, it
|
||||
|
204
src/Mapper/Tree/Message/MessageBuilder.php
Normal file
204
src/Mapper/Tree/Message/MessageBuilder.php
Normal file
@ -0,0 +1,204 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Mapper\Tree\Message;
|
||||
|
||||
use LanguageServerProtocol\MessageType;
|
||||
use RuntimeException;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Can be used to easily create an instance of (error) message.
|
||||
*
|
||||
* ```php
|
||||
* $message = MessageBuilder::newError('Some message with {some_parameter}.')
|
||||
* ->withCode('some_code')
|
||||
* ->withParameter('some_parameter', 'some_value')
|
||||
* ->build();
|
||||
* ```
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @template MessageType of Message
|
||||
*/
|
||||
final class MessageBuilder
|
||||
{
|
||||
private bool $isError = false;
|
||||
|
||||
private string $body;
|
||||
|
||||
private string $code = 'unknown';
|
||||
|
||||
/** @var array<string, string> */
|
||||
private array $parameters = [];
|
||||
|
||||
private function __construct(string $body)
|
||||
{
|
||||
$this->body = $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self<Message>
|
||||
*/
|
||||
public static function new(string $body): self
|
||||
{
|
||||
return new self($body);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self<ErrorMessage>
|
||||
*/
|
||||
public static function newError(string $body): self
|
||||
{
|
||||
$instance = new self($body);
|
||||
$instance->isError = true;
|
||||
|
||||
/** @var self<ErrorMessage> */
|
||||
return $instance;
|
||||
}
|
||||
|
||||
public static function from(Throwable $error): ErrorMessage
|
||||
{
|
||||
if ($error instanceof ErrorMessage) {
|
||||
return $error;
|
||||
}
|
||||
|
||||
return self::newError($error->getMessage())
|
||||
->withCode((string)$error->getCode())
|
||||
->build();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self<MessageType>
|
||||
*/
|
||||
public function withBody(string $body): self
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->body = $body;
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
public function body(): string
|
||||
{
|
||||
return $this->body;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self<MessageType>
|
||||
*/
|
||||
public function withCode(string $code): self
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->code = $code;
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
public function code(): string
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self<MessageType>
|
||||
*/
|
||||
public function withParameter(string $name, string $value): self
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->parameters[$name] = $value;
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function parameters(): array
|
||||
{
|
||||
return $this->parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MessageType&HasCode&HasParameters
|
||||
*/
|
||||
public function build(): Message
|
||||
{
|
||||
/** @var MessageType&HasCode&HasParameters */
|
||||
return $this->isError
|
||||
? $this->buildErrorMessage()
|
||||
: $this->buildMessage();
|
||||
}
|
||||
|
||||
private function buildMessage(): Message
|
||||
{
|
||||
return new class ($this->body, $this->code, $this->parameters) implements Message, HasCode, HasParameters {
|
||||
private string $body;
|
||||
|
||||
private string $code;
|
||||
|
||||
/** @var array<string, string> */
|
||||
private array $parameters;
|
||||
|
||||
/**
|
||||
* @param array<string, string> $parameters
|
||||
*/
|
||||
public function __construct(string $body, string $code, array $parameters)
|
||||
{
|
||||
$this->body = $body;
|
||||
$this->code = $code;
|
||||
$this->parameters = $parameters;
|
||||
}
|
||||
|
||||
public function body(): string
|
||||
{
|
||||
return $this->body;
|
||||
}
|
||||
|
||||
public function code(): string
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
public function parameters(): array
|
||||
{
|
||||
return $this->parameters;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private function buildErrorMessage(): ErrorMessage
|
||||
{
|
||||
return new class ($this->body, $this->code, $this->parameters) extends RuntimeException implements ErrorMessage, HasCode, HasParameters {
|
||||
/** @var array<string, string> */
|
||||
private array $parameters;
|
||||
|
||||
/**
|
||||
* @param array<string, string> $parameters
|
||||
*/
|
||||
public function __construct(string $body, string $code, array $parameters)
|
||||
{
|
||||
parent::__construct($body);
|
||||
|
||||
$this->code = $code;
|
||||
$this->parameters = $parameters;
|
||||
}
|
||||
|
||||
public function body(): string
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
public function code(): string
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
public function parameters(): array
|
||||
{
|
||||
return $this->parameters;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -7,7 +7,11 @@ namespace CuyZ\Valinor\Mapper\Tree\Message;
|
||||
use RuntimeException;
|
||||
use Throwable;
|
||||
|
||||
/** @api */
|
||||
/**
|
||||
* @api
|
||||
*
|
||||
* @deprecated Use {@see \CuyZ\Valinor\Mapper\Tree\Message\MessageBuilder}
|
||||
*/
|
||||
final class ThrowableMessage extends RuntimeException implements ErrorMessage, HasCode
|
||||
{
|
||||
public static function new(string $message, string $code): self
|
||||
|
@ -291,7 +291,7 @@ final class MapperBuilder
|
||||
* (new \CuyZ\Valinor\MapperBuilder())
|
||||
* ->filterExceptions(function (Throwable $exception) {
|
||||
* if ($exception instanceof \Webmozart\Assert\InvalidArgumentException) {
|
||||
* return \CuyZ\Valinor\Mapper\Tree\Message\ThrowableMessage::from($exception);
|
||||
* return \CuyZ\Valinor\Mapper\Tree\Message\MessageBuilder::from($exception);
|
||||
* }
|
||||
*
|
||||
* // If the exception should not be caught by this library, it must
|
||||
|
86
tests/Unit/Mapper/Tree/Message/MessageBuilderTest.php
Normal file
86
tests/Unit/Mapper/Tree/Message/MessageBuilderTest.php
Normal file
@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Tests\Unit\Mapper\Tree\Message;
|
||||
|
||||
use CuyZ\Valinor\Mapper\Tree\Message\HasCode;
|
||||
use CuyZ\Valinor\Mapper\Tree\Message\MessageBuilder;
|
||||
use CuyZ\Valinor\Tests\Fake\Mapper\Tree\Message\FakeErrorMessage;
|
||||
use Exception;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class MessageBuilderTest extends TestCase
|
||||
{
|
||||
public function test_body_can_be_retrieved(): void
|
||||
{
|
||||
$message = MessageBuilder::new('some message body');
|
||||
$messageError = MessageBuilder::newError('some error message body');
|
||||
|
||||
self::assertSame('some message body', $message->body());
|
||||
self::assertSame('some message body', $message->build()->body());
|
||||
|
||||
self::assertSame('some error message body', $messageError->body());
|
||||
self::assertSame('some error message body', $messageError->build()->body());
|
||||
|
||||
$message = $message->withBody('some new message body');
|
||||
$messageError = $messageError->withBody('some new error message body');
|
||||
|
||||
self::assertSame('some new message body', $message->body());
|
||||
self::assertSame('some new message body', $message->build()->body());
|
||||
|
||||
self::assertSame('some new error message body', $messageError->body());
|
||||
self::assertSame('some new error message body', $messageError->build()->body());
|
||||
}
|
||||
|
||||
public function test_code_can_be_retrieved(): void
|
||||
{
|
||||
$message = MessageBuilder::new('some message body');
|
||||
$message = $message->withCode('some_code');
|
||||
|
||||
self::assertSame('some_code', $message->code());
|
||||
self::assertSame('some_code', $message->build()->code());
|
||||
}
|
||||
|
||||
public function test_parameters_can_be_retrieved(): void
|
||||
{
|
||||
$message = MessageBuilder::new('some message body');
|
||||
$message = $message
|
||||
->withParameter('parameter_a', 'valueA')
|
||||
->withParameter('parameter_b', 'valueB');
|
||||
|
||||
self::assertSame(['parameter_a' => 'valueA', 'parameter_b' => 'valueB'], $message->parameters());
|
||||
self::assertSame(['parameter_a' => 'valueA', 'parameter_b' => 'valueB'], $message->build()->parameters());
|
||||
}
|
||||
|
||||
public function test_modifiers_return_clone_instances(): void
|
||||
{
|
||||
$messageA = MessageBuilder::new('some message body');
|
||||
$messageB = $messageA->withBody('some new message body');
|
||||
$messageC = $messageB->withCode('some_code');
|
||||
$messageD = $messageC->withParameter('parameter_a', 'valueA');
|
||||
|
||||
self::assertNotSame($messageA, $messageB);
|
||||
self::assertNotSame($messageB, $messageC);
|
||||
self::assertNotSame($messageC, $messageD);
|
||||
}
|
||||
|
||||
public function test_from_throwable_build_error_message(): void
|
||||
{
|
||||
$exception = new Exception('some error message', 1664450422);
|
||||
|
||||
$message = MessageBuilder::from($exception);
|
||||
|
||||
self::assertSame('some error message', $message->body());
|
||||
self::assertInstanceOf(HasCode::class, $message);
|
||||
self::assertSame('1664450422', $message->code());
|
||||
}
|
||||
|
||||
public function test_from_error_message_returns_same_instance(): void
|
||||
{
|
||||
$error = new FakeErrorMessage();
|
||||
$message = MessageBuilder::from($error);
|
||||
|
||||
self::assertSame($error, $message);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user