src/Discovery/Generator.php

Summary

Maintainability
A
1 hr
Test Coverage
<?php
/**
 * This file is part of Railt package.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
declare(strict_types=1);

namespace Railt\Discovery;

use Composer\Composer;
use Composer\IO\IOInterface;
use Phplrt\Io\File;
use Phplrt\Io\Readable;
use Railt\Discovery\Composer\DiscoveryConfiguration;
use Railt\Discovery\Composer\DiscoverySection;
use Railt\Discovery\Composer\Package;
use Railt\Discovery\Composer\Reader;
use Railt\Discovery\Composer\Section;
use Railt\Discovery\Exception\ValidationException;
use Railt\Json\Exception\JsonException;
use Railt\Json\Json;

/**
 * Class Generator
 */
class Generator
{
    /**
     * @var Composer
     */
    private $composer;

    /**
     * @var IOInterface
     */
    private $io;

    /**
     * Generator constructor.
     *
     * @param Composer $composer
     * @param IOInterface $io
     */
    public function __construct(Composer $composer, IOInterface $io)
    {
        $this->composer = $composer;
        $this->io = $io;
    }

    /**
     * @param array $data
     * @return Readable
     * @throws JsonException
     */
    public function save(array $data): Readable
    {
        $config = $this->composer->getConfig();
        $directory = $config->get('vendor-dir');

        Json::write($directory . '/discovery.json', $data);

        return File::fromPathname($directory . '/discovery.json');
    }

    /**
     * @return array
     * @throws ValidationException
     * @throws \Throwable
     */
    public function run(): array
    {
        $reader = new Reader($this->composer);

        $sections = [];

        /**
         * @var Section $section
         * @var DiscoveryConfiguration[] $configs
         */
        foreach ($this->collect($reader, $this->io) as $section => $configs) {
            $name = $section->getName();

            if (! isset($sections[$name])) {
                $sections[$name] = [];
            }

            $value = $section->get();

            foreach ($configs as $config) {
                $value = $config->filter($value);
            }

            /** @noinspection SlowArrayOperationsInLoopInspection */
            $sections[$name] = \array_merge_recursive($sections[$name], $value);
        }

        return $sections;
    }

    /**
     * @param Reader $reader
     * @param IOInterface $io
     * @return \Traversable|DiscoveryConfiguration[][]
     * @throws ValidationException
     * @throws \Throwable
     */
    private function collect(Reader $reader, IOInterface $io): \Traversable
    {
        $sections = $this->loadConfigs($reader);

        foreach ($sections as $name => $configs) {
            $io->write(\sprintf('Discovery: <info>%s</info>', $name));

            /**
             * @var Package $package
             * @var Section $section
             */
            foreach ($this->readSection($name, $reader) as $package => $section) {
                $io->write(\sprintf('    import from <comment>%s</comment>: ', $package->getName()), false);

                try {
                    foreach ($configs as $i => $config) {
                        $config->validate($section);
                    }
                    $io->write('<info>OK</info>');
                } catch (ValidationException $e) {
                    // TODO
                    throw $e;
                } catch (\Throwable $e) {
                    $io->write('<error> ERROR </error>');

                    throw $e;
                }

                yield $section => $configs;
            }
        }
    }

    /**
     * @param Reader $reader
     * @return array|DiscoveryConfiguration[][]
     */
    private function loadConfigs(Reader $reader): array
    {
        $sections = [];

        foreach ($this->readConfigs($reader) as $name => $config) {
            if (! isset($sections[$name])) {
                $sections[$name] = [];
            }

            $sections[$name][] = $config;
        }

        return $sections;
    }

    /**
     * @param Reader $reader
     * @return \Traversable|DiscoveryConfiguration[]
     */
    private function readConfigs(Reader $reader): \Traversable
    {
        foreach ($reader->getPackages() as $package) {
            $section = $package->getSection(DiscoverySection::KEY_DISCOVERY);

            if ($section !== null) {
                yield from $section->getConfiguration();
            }
        }
    }

    /**
     * @param string $name
     * @param Reader $reader
     * @return \Traversable
     */
    private function readSection(string $name, Reader $reader): \Traversable
    {
        foreach ($reader->getPackages() as $package) {
            $section = $package->getSection($name);

            if ($section) {
                yield $package => $section;
            }
        }
    }
}