$messages */ public function __construct( public int $count = 0, public array $messages = [], ) {} public static function none(): self { return new self; } /** * @param array $meta */ public static function fromItemMeta(array $meta, FidelityState $fidelity, bool $usesFallback = false): self { $messages = []; $warnings = $meta['warnings'] ?? null; if (is_array($warnings)) { foreach ($warnings as $warning) { if (is_string($warning) && trim($warning) !== '') { $messages[] = trim($warning); } } } if ($fidelity === FidelityState::ReferenceOnly) { $messages[] = 'Metadata-only evidence was captured for this item.'; } if ($fidelity === FidelityState::Unsupported || $usesFallback) { $messages[] = 'A fallback renderer is being used for this item.'; } $messages = self::uniqueMessages($messages); return new self( count: count($messages), messages: $messages, ); } /** * @param array $reasons */ public static function fromReasonMap(array $reasons): self { $messages = []; foreach ($reasons as $reason => $count) { if (! is_string($reason) || ! is_numeric($count) || (int) $count <= 0) { continue; } $messages[] = sprintf('%s (%d)', self::humanizeReason($reason), (int) $count); } return new self( count: array_sum(array_map(static fn (mixed $value): int => is_numeric($value) ? (int) $value : 0, $reasons)), messages: self::uniqueMessages($messages), ); } /** * @param array $summaries */ public static function merge(array $summaries): self { $count = 0; $messages = []; foreach ($summaries as $summary) { $count += $summary->count; $messages = [...$messages, ...$summary->messages]; } $messages = self::uniqueMessages($messages); if ($count === 0) { $count = count($messages); } return new self( count: $count, messages: $messages, ); } public function withMessage(string $message): self { $message = trim($message); if ($message === '') { return $this; } $messages = self::uniqueMessages([...$this->messages, $message]); return new self( count: max($this->count, count($messages)), messages: $messages, ); } public function hasGaps(): bool { return $this->count > 0 || $this->messages !== []; } public function badgeState(): string { return $this->hasGaps() ? 'gaps_present' : 'clear'; } /** * @return array{count: int, has_gaps: bool, messages: list, badge_state: string} */ public function toArray(): array { return [ 'count' => $this->count, 'has_gaps' => $this->hasGaps(), 'messages' => $this->messages, 'badge_state' => $this->badgeState(), ]; } /** * @param list $messages * @return list */ private static function uniqueMessages(array $messages): array { return array_values(array_unique(array_values(array_filter( array_map(static fn (string $message): string => trim($message), $messages), static fn (string $message): bool => $message !== '', )))); } private static function humanizeReason(string $reason): string { return Str::of($reason) ->replace(['_', '-'], ' ') ->headline() ->toString(); } }