src/Foundation/Webonyx/Builder/ScalarBuilder.php

Summary

Maintainability
B
4 hrs
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\Foundation\Webonyx\Builder;

use Carbon\Carbon;
use GraphQL\Error\InvariantViolation;
use GraphQL\Type\Definition\CustomScalarType;
use GraphQL\Type\Definition\ScalarType;
use GraphQL\Type\Definition\Type;
use Railt\Dumper\TypeDumper;
use Railt\Foundation\Webonyx\Exception\ParsingException;
use Railt\Foundation\Webonyx\Exception\SerializationException;
use Railt\SDL\Contracts\Definitions\ScalarDefinition;

/**
 * Class ScalarBuilder
 *
 * @property ScalarDefinition $reflection
 */
class ScalarBuilder extends Builder
{
    /**
     * @return ScalarType
     */
    public function build(): ScalarType
    {
        return $this->builtin($this->reflection, function (ScalarDefinition $reflection) {
            return $this->create($reflection);
        });
    }

    /**
     * @param ScalarDefinition $reflection
     * @param \Closure $otherwise
     * @return ScalarType
     */
    private function builtin(ScalarDefinition $reflection, \Closure $otherwise): ScalarType
    {
        switch ($reflection->getName()) {
            case 'String':
                return Type::string();

            case 'ID':
                return Type::id();

            case 'Boolean':
                return Type::boolean();

            case 'Int':
                return Type::int();

            case 'Float':
                return Type::float();

            case 'DateTime':
                return $this->createDateTime($reflection);
        }

        return $otherwise($reflection);
    }

    /**
     * @param ScalarDefinition $reflection
     * @return ScalarType
     * @throws InvariantViolation
     */
    private function createDateTime(ScalarDefinition $reflection): ScalarType
    {
        $serializeDateTime = function ($value): string {
            switch (true) {
                case \is_string($value):
                    return Carbon::parse($value)->toRfc3339String();

                case \is_int($value):
                    return Carbon::createFromTimestamp($value)->toRfc3339String();

                case $value instanceof \DateTimeInterface:
                    return Carbon::instance($value)->toRfc3339String();
            }

            $error = 'Can not serialize type %s to valida DateTime string';

            $exception = new SerializationException(\sprintf($error, TypeDumper::render($value)));
            $exception->publish();

            throw $exception;
        };

        $parseDateTime = function ($value): \DateTimeInterface {
            switch (true) {
                case \is_string($value):
                    return Carbon::parse($value);

                case \is_int($value):
                    return Carbon::createFromTimestamp($value);

                case $value instanceof \DateTimeInterface:
                    return Carbon::instance($value);
            }

            $error = '%s is not a valid DateTime type';

            $exception = new ParsingException(\sprintf($error, TypeDumper::render($value)));
            $exception->publish();

            throw $exception;
        };

        return new CustomScalarType([
            'name'         => $reflection->getName(),
            'description'  => $reflection->getDescription(),
            'serialize'    => function ($value) use ($serializeDateTime): string {
                return $serializeDateTime($value);
            },
            'parseValue'   => function ($value) use ($parseDateTime): \DateTimeInterface {
                return $parseDateTime($value);
            },
            'parseLiteral' => function ($value) use ($parseDateTime): \DateTimeInterface {
                return $parseDateTime($value);
            },
        ]);
    }

    /**
     * @param ScalarDefinition $reflection
     * @return ScalarType
     * @throws InvariantViolation
     */
    private function create(ScalarDefinition $reflection): ScalarType
    {
        return new CustomScalarType([
            'name'         => $reflection->getName(),
            'description'  => $reflection->getDescription(),
            'serialize'    => function ($value) {
                return $this->serialize($value);
            },
            'parseValue'   => function ($value) {
                return $this->parse($value);
            },
            'parseLiteral' => function ($value) {
                return $this->parse($value);
            },
        ]);
    }

    /**
     * @param mixed $value
     * @return mixed
     */
    private function serialize($value)
    {
        return $value;
    }

    /**
     * @param mixed $value
     * @return mixed
     */
    private function parse($value)
    {
        if (\is_object($value) && \property_exists($value, 'value')) {
            return $value->value;
        }

        return $value;
    }
}