diff --git a/src/Mapper/Tree/Message/Formatter/MessageFormatter.php b/src/Mapper/Tree/Message/Formatter/MessageFormatter.php new file mode 100644 index 0000000..1a589b4 --- /dev/null +++ b/src/Mapper/Tree/Message/Formatter/MessageFormatter.php @@ -0,0 +1,12 @@ + 'new content / previous code was: %1$s', + * + * // Will match if the given message has this exact content + * 'Some message content' => 'new content / previous message: %2$s', + * + * // Will match if the given message is an instance of `SomeError` + * SomeError::class => ' + * - Original code of the message: %1$s + * - Original content of the message: %2$s + * - Node type: %3$s + * - Node name: %4$s + * - Node path: %5$s + * ', + * + * // A callback can be used to get access to the message instance + * OtherError::class => function (NodeMessage $message): string { + * if ((string)$message->type() === 'string|int') { + * // … + * } + * + * return 'Some message content'; + * }, + * + * // For greedy operation, it is advised to use a lazy-callback + * 'foo' => fn () => $this->translator->translate('foo.bar'), + * ])) + * ->defaultsTo('some default message') + * // …or… + * ->defaultsTo(fn () => $this->translator->translate('default_message')); + * + * $content = $formatter->format($message); + * ``` + */ +final class MessageMapFormatter implements MessageFormatter +{ + /** @var array */ + private array $map; + + /** @var null|string|callable(NodeMessage): string */ + private $default; + + /** + * @param array $map + */ + public function __construct(array $map) + { + $this->map = $map; + } + + public function format(NodeMessage $message): string + { + $target = $this->target($message); + $text = is_callable($target) ? $target($message) : $target; + + return $message->format($text); + } + + /** + * @param string|callable(NodeMessage): string $default + */ + public function defaultsTo($default): self + { + $clone = clone $this; + $clone->default = $default; + + return $clone; + } + + /** + * @return string|callable(NodeMessage): string + */ + private function target(NodeMessage $message) + { + return $this->map[$message->code()] + ?? $this->map[(string)$message] + ?? $this->map[get_class($message->originalMessage())] + ?? $this->default + ?? (string)$message; + } +} diff --git a/tests/Unit/Mapper/Tree/Message/Formatter/MessageMapFormatterTest.php b/tests/Unit/Mapper/Tree/Message/Formatter/MessageMapFormatterTest.php new file mode 100644 index 0000000..cf09842 --- /dev/null +++ b/tests/Unit/Mapper/Tree/Message/Formatter/MessageMapFormatterTest.php @@ -0,0 +1,70 @@ + 'foo', + ]); + + self::assertSame('foo', $formatter->format($message)); + } + + public function test_format_finds_code_returns_formatted_content_from_callback(): void + { + $message = new NodeMessage(FakeNode::any(), new FakeMessage()); + $formatter = new MessageMapFormatter([ + 'some_code' => fn (NodeMessage $message) => "foo $message", + ]); + + self::assertSame('foo some message', $formatter->format($message)); + } + + public function test_format_finds_content_returns_formatted_content(): void + { + $message = new NodeMessage(FakeNode::any(), new FakeMessage()); + $formatter = new MessageMapFormatter([ + 'some message' => 'foo', + ]); + + self::assertSame('foo', $formatter->format($message)); + } + + public function test_format_finds_class_name_returns_formatted_content(): void + { + $message = new NodeMessage(FakeNode::any(), new FakeMessage()); + $formatter = new MessageMapFormatter([ + FakeMessage::class => 'foo', + ]); + + self::assertSame('foo', $formatter->format($message)); + } + + public function test_format_does_not_find_any_returns_default(): void + { + $message = new NodeMessage(FakeNode::any(), new FakeMessage()); + $formatter = (new MessageMapFormatter([]))->defaultsTo('foo'); + + self::assertSame('foo', $formatter->format($message)); + } + + public function test_format_does_not_find_any_returns_message_content(): void + { + $message = new NodeMessage(FakeNode::any(), new FakeMessage()); + $formatter = new MessageMapFormatter([]); + + self::assertSame('some message', $formatter->format($message)); + } +}