-
-
Notifications
You must be signed in to change notification settings - Fork 74
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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(); ```
- Loading branch information
Showing
6 changed files
with
307 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
} |