respect-validation/src-dev/Markdown/Linters/ValidatorTemplatesLinter.php
Alexandre Gomes Gaigalas bd48bdcda4 Lint Changelog format in validator docs
Introduces a Markdown linter for checking the Changelog format.

"See Also" was transformed into a section to make it easier to
handle it with the `Content` class. The "Related" linter was
simplified to reflect that change too.

An additional "alignment" parameter was added to markdown table
generators, allowing the padding and headers to be explicitly
marked with a specific left (-1), middle (0) or right(1)
alignment.

Existing files were fixed using the `fix` option after the
changes.
2026-01-26 19:11:00 +00:00

104 lines
3.1 KiB
PHP

<?php
/*
* SPDX-License-Identifier: MIT
* SPDX-FileCopyrightText: (c) Respect Project Contributors
* SPDX-FileContributor: Henrique Moody <henriquemoody@gmail.com>
*/
declare(strict_types=1);
namespace Respect\Dev\Markdown\Linters;
use ReflectionClass;
use Respect\Dev\Markdown\Content;
use Respect\Dev\Markdown\File;
use Respect\Dev\Markdown\Linter;
use Respect\Validation\Message\Template;
use UnexpectedValueException;
use function array_find_key;
use function basename;
use function preg_match;
use function preg_replace;
use function sprintf;
use function str_contains;
use function str_starts_with;
final readonly class ValidatorTemplatesLinter implements Linter
{
public function lint(File $file): File
{
if (!str_contains($file->filename, '/validators/')) {
return $file;
}
$validator = basename($file->filename, '.md');
$templates = $this->getTemplates($validator);
if ($templates === []) {
return $file;
}
$descriptions = [];
try {
$currentTemplates = $file->content->getSection('## Templates');
$templateLines = $currentTemplates->toArray();
foreach ($templateLines as $index => $currentLine) {
if (!str_starts_with($currentLine, '### ')) {
continue;
}
$id = preg_replace('/^### `.+::([A-Z_]+)`/', '$1', $currentLine);
if (!isset($templateLines[$index + 2]) || !preg_match('/^[A-Z]/', $templateLines[$index + 2])) {
continue;
}
$descriptions[$id] = $templateLines[$index + 2];
}
} catch (UnexpectedValueException) {
// No existing Templates section, that's fine
}
$content = new Content();
$content->h2('Templates');
foreach ($templates as $id => $template) {
$content->h3(sprintf('`%s::%s`', $validator, $id));
if (isset($descriptions[$id])) {
$content->paragraph($descriptions[$id]);
$content->emptyLine();
}
$content->table(
['Mode', 'Template'],
[
['`default`', $template->default],
['`inverted`', $template->inverted],
],
alignment: [1, -1],
);
}
return $file->withContent($file->content->withSection($content));
}
/** @return array<string, Template> */
private function getTemplates(string $validator): array
{
$reflectionClass = new ReflectionClass('Respect\\Validation\\Validators\\' . $validator);
$constants = $reflectionClass->getConstants();
$templates = [];
foreach ($reflectionClass->getAttributes(Template::class) as $attribute) {
$template = $attribute->newInstance();
$id = array_find_key($constants, static fn($constant) => $template->id === $constant);
if ($id === null) {
continue;
}
$templates[$id] = $template;
}
return $templates;
}
}