<commit_before><?php

declare(strict_types=1);

namespace Chubbyphp\Validation\Error;

final class NestedErrorMessages implements ErrorMessagesInterface
{
    /**
     * @var ErrorInterface[]
     */
    private $errors;

    /**
     * @var callable
     */
    private $translate;

    /**
     * @var array
     */
    private $nestedErrorMessages;

    /**
     * @param ErrorInterface[] $errors
     */
    public function __construct(array $errors, callable $translate)
    {
        $this->errors = [];
        foreach ($errors as $error) {
            $this->addError($error);
        }
        $this->translate = $translate;
    }

    /**
     * @param ErrorInterface $error
     */
    private function addError(ErrorInterface $error)
    {
        $this->errors[] = $error;
    }

    /**
     * @return array
     */
    public function getMessages(): array
    {
        if (null === $this->nestedErrorMessages) {
            $this->nestedErrorMessages = [];
            foreach ($this->errors as $error) {
                $this->assignErrorMessage($this->nestedErrorMessages, $error);
            }
        }

        return $this->nestedErrorMessages;
    }

    /**
     * @param array $node
     * @param ErrorInterface $error
     */
    private function assignErrorMessage(array &$node, ErrorInterface $error)
    {
        $pathParts = $this->parsePath($error->getPath());
        foreach ($pathParts as $pathPart) {
            if (!isset($node[$pathPart])) {
                $node[$pathPart] = [];
            }
            $node = &$node[$pathPart];
        }

        $translate = $this->translate;

        $node[] = $translate($error->getKey(), $error->getArgs());
    }

    /**
     * @param string $path
     * @return array
     */
    private function parsePath(string $path): array
    {
        $pathParts = [];
        foreach (explode('.', $path) as $pathPart) {
            $matches = [];
            if (1 === preg_match('/^(([^\[]+)\[(\d+)\])$/', $pathPart, $matches)) {
                $pathParts[] = $matches[2];
                $pathParts[] = (int) $matches[3];
            } else {
                $pathParts[] = $pathPart;
            }
        }

        return $pathParts;
    }
}<commit_msg>on array / collection validation the key _all means for all elements<commit_after><?php

declare(strict_types=1);

namespace Chubbyphp\Validation\Error;

final class NestedErrorMessages implements ErrorMessagesInterface
{
    /**
     * @var ErrorInterface[]
     */
    private $errors;

    /**
     * @var callable
     */
    private $translate;

    /**
     * @var array
     */
    private $nestedErrorMessages;

    /**
     * @param ErrorInterface[] $errors
     */
    public function __construct(array $errors, callable $translate)
    {
        $this->errors = [];
        foreach ($errors as $error) {
            $this->addError($error);
        }
        $this->translate = $translate;
    }

    /**
     * @param ErrorInterface $error
     */
    private function addError(ErrorInterface $error)
    {
        $this->errors[] = $error;
    }

    /**
     * @return array
     */
    public function getMessages(): array
    {
        if (null === $this->nestedErrorMessages) {
            $this->nestedErrorMessages = [];
            foreach ($this->errors as $error) {
                $this->assignErrorMessage($this->nestedErrorMessages, $error);
            }
        }

        return $this->nestedErrorMessages;
    }

    /**
     * @param array $node
     * @param ErrorInterface $error
     */
    private function assignErrorMessage(array &$node, ErrorInterface $error)
    {
        $pathParts = $this->parsePath($error->getPath());
        foreach ($pathParts as $pathPart) {
            if (!isset($node[$pathPart])) {
                $node[$pathPart] = [];
            }
            $node = &$node[$pathPart];
        }

        $translate = $this->translate;

        $node[] = $translate($error->getKey(), $error->getArgs());
    }

    /**
     * @param string $path
     * @return array
     */
    private function parsePath(string $path): array
    {
        $pathParts = [];
        foreach (explode('.', $path) as $pathPart) {
            $matches = [];
            if (1 === preg_match('/^(([^\[]+)\[(\d+|_all)\])$/', $pathPart, $matches)) {
                $pathParts[] = $matches[2];
                $pathParts[] = (int) $matches[3];
            } else {
                $pathParts[] = $pathPart;
            }
        }

        return $pathParts;
    }
}
{'fraction_non_alphanumeric': 0.15564373897707232, 'fraction_numerical': 0.001763668430335097, 'mean_word_length': 1.8303181534622583, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1536697', 'n_tokens_mistral': 1306, 'n_tokens_neox': 1265, 'n_words': 260}