Skip to content

Instantly share code, notes, and snippets.

@vudaltsov
Created December 31, 2022 03:41
Show Gist options
  • Save vudaltsov/05afd6a12f51fa30ed20d1555b89244a to your computer and use it in GitHub Desktop.
Save vudaltsov/05afd6a12f51fa30ed20d1555b89244a to your computer and use it in GitHub Desktop.
Envelope
<?php
/**
* @psalm-immutable
* @template TResult
*/
interface Message
{
}
/**
* @psalm-immutable
*/
interface Stamp
{
}
/**
* @psalm-immutable
* @template TResult
* @template TMessage of Message<TResult>
*/
final class Envelope
{
/**
* @param TMessage $message
* @param array<class-string<Stamp>, Stamp> $stamps
*/
private function __construct(
public readonly Message $message,
public readonly array $stamps,
) {
}
/**
* @psalm-pure
* @template TTResult
* @template TTMessage of Message<TTResult>
* @param TTMessage $message
* @return self<TTResult, TTMessage>
*/
public static function wrap(Message $message, Stamp ...$stamps): self
{
$stampsByClass = [];
foreach ($stamps as $stamp) {
$stampsByClass[$stamp::class] = $stamp;
}
return new self($message, $stampsByClass);
}
/**
* @template TTMessage of Message
* @param TTMessage $message
* @return self<TResult, TTMessage>
*/
public function withMessage(Message $message): self
{
return new self($message, $this->stamps);
}
/**
* @template TStamp of Stamp
* @param class-string<TStamp> $class
*/
public function hasStamp(string $class): bool
{
return isset($this->stamps[$class]);
}
/**
* @template TStamp of Stamp
* @param class-string<TStamp> $class
* @return TStamp
*/
public function stamp(string $class): Stamp
{
return $this->stampOrNull($class) ?? throw new \RuntimeException(sprintf(
'Stamp "%s" is not set on the envelope with message "%s".',
$class,
$this->message::class,
));
}
/**
* @template TStamp of Stamp
* @param class-string<TStamp> $class
* @return ?TStamp
*/
public function stampOrNull(string $class): ?Stamp
{
/** @var ?TStamp */
return $this->stamps[$class] ?? null;
}
/**
* @return self<TResult, TMessage>
*/
public function withStamps(Stamp ...$stamps): self
{
$mergedStamps = $this->stamps;
foreach ($stamps as $stamp) {
$mergedStamps[$stamp::class] = $stamp;
}
return new self($this->message, $mergedStamps);
}
/**
* @param class-string<Stamp> ...$classes
* @return self<TResult, TMessage>
*/
public function withoutStamps(string ...$classes): self
{
$stamps = $this->stamps;
foreach ($classes as $class) {
unset($stamps[$class]);
}
return new self($this->message, $stamps);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment