chevere/chevere

View on GitHub
src/Dependent/Dependencies.php

Summary

Maintainability
A
35 mins
Test Coverage
<?php

/*
 * This file is part of Chevere.
 *
 * (c) Rodolfo Berrios <rodolfo@chevere.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace Chevere\Dependent;

use Chevere\ClassMap\ClassMap;
use Chevere\ClassMap\Interfaces\ClassMapInterface;
use Chevere\Dependent\Interfaces\DependenciesInterface;
use Chevere\Message\Message;
use Chevere\Throwable\Exceptions\OutOfBoundsException;
use Chevere\Throwable\Exceptions\OverflowException;
use Ds\Set;
use Iterator;

final class Dependencies implements DependenciesInterface
{
    private ClassMapInterface $classMap;

    private Set $keys;

    public function __construct(string ...$dependencies)
    {
        $this->classMap = new ClassMap();
        $this->keys = new Set();
        $this->putDependencies(...$dependencies);
    }

    public function withPut(string ...$dependencies): DependenciesInterface
    {
        $new = clone $this;
        $new->putDependencies(...$dependencies);

        return $new;
    }

    public function withMerge(DependenciesInterface $dependencies): DependenciesInterface
    {
        $new = clone $this;
        foreach ($dependencies->getIterator() as $name => $className) {
            if ($new->keys->contains($name)) {
                $expected = $new->key($name);
                if ($expected !== $className) {
                    throw new OverflowException(
                        message: (new Message('Attempting to re-declare named dependency %named% type from %expected% to %provided%'))
                            ->strong('%named%', $name)
                            ->code('%expected%', $expected)
                            ->code('%provided%', $className)
                    );
                }
            } else {
                $new->putDependency($className, $name);
            }
        }

        return $new;
    }

    public function hasKey(string $key): bool
    {
        return $this->keys->contains($key);
    }

    /**
     * @throws OutOfBoundsException
     */
    public function key(string $key): string
    {
        return $this->classMap->className($key);
    }

    public function keys(): array
    {
        return $this->keys->toArray();
    }

    public function count(): int
    {
        return $this->classMap->count();
    }

    #[\ReturnTypeWillChange]
    public function getIterator(): Iterator
    {
        foreach ($this->classMap->getIterator() as $className => $key) {
            yield $key => $className;
        }
    }

    private function putDependencies(string ...$dependencies): void
    {
        foreach ($dependencies as $name => $className) {
            $this->putDependency($className, strval($name));
        }
    }

    private function putDependency(string $className, string $name): void
    {
        $this->classMap = $this->classMap
            ->withPut($className, $name);
        $this->keys->add($name);
    }
}