Test Failed
Push — master ( 16c39a...9c7d46 )
by Kirill
07:07
created

ImportBuilder::tryOpen()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 2
dl 0
loc 16
rs 9.7333
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file is part of Railt package.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
declare(strict_types=1);
9
10
namespace Railt\SDL\Frontend\Builder;
11
12
use Railt\Io\Exception\NotReadableException;
13
use Railt\Io\File;
14
use Railt\Io\Readable;
15
use Railt\Parser\Ast\RuleInterface;
16
use Railt\SDL\Exception\FileNotReadableException;
17
use Railt\SDL\Frontend\AST\Scalar\ScalarInterface;
18
use Railt\SDL\Frontend\Context\ContextInterface;
19
use Railt\SDL\Frontend\Record\RecordInterface;
20
use Railt\SDL\Frontend\Type\TypeNameInterface;
21
22
/**
23
 * Class IncludeBuilder
24
 */
25
class ImportBuilder extends BaseBuilder
26
{
27
    /**
28
     * @param RuleInterface $rule
29
     * @return bool
30
     */
31
    public function match(RuleInterface $rule): bool
32
    {
33
        return $rule->getName() === 'ImportDefinition';
34
    }
35
36
    /**
37
     * @param ContextInterface $ctx
38
     * @param RuleInterface $rule
39
     * @return \Generator|RecordInterface|TypeNameInterface|void
40
     */
41
    public function reduce(ContextInterface $ctx, RuleInterface $rule)
42
    {
43
        /** @var ScalarInterface $value */
44
        $value = $rule->first('> #StringValue');
45
46
        yield from $this->load($this->include($ctx, $value));
47
    }
48
49
    /**
50
     * @param ContextInterface $ctx
51
     * @param ScalarInterface|RuleInterface $inclusion
52
     * @return Readable
53
     */
54
    private function include(ContextInterface $ctx, ScalarInterface $inclusion): Readable
55
    {
56
        try {
57
            return $this->tryOpen($ctx, $inclusion);
58
        } catch (FileNotReadableException $e) {
59
            throw $e->throwsIn($ctx->getFile(), $inclusion->getOffset());
60
        }
61
    }
62
63
    /**
64
     * @param ContextInterface $ctx
65
     * @param ScalarInterface $inclusion
66
     * @return Readable
67
     * @throws FileNotReadableException
68
     */
69
    private function tryOpen(ContextInterface $ctx, ScalarInterface $inclusion): Readable
70
    {
71
        $pathname = $this->getPathname($ctx->getFile(), $inclusion->toPrimitive());
72
73
        if ($ctx->getFile()->isFile()) {
74
            try {
75
                return File::fromPathname($pathname);
76
            } catch (NotReadableException $e) {
77
                $error = 'File "%s" not found or not readable.';
78
                throw new FileNotReadableException(\sprintf($error, $pathname), $e->getCode());
79
            }
80
        }
81
82
        $error = 'It is impossible to include an external file "%s" inside a non-physical file';
83
        throw new FileNotReadableException(\sprintf($error, $inclusion->toPrimitive()));
84
    }
85
86
    /**
87
     * @param Readable $file
88
     * @param string $inclusion
89
     * @return string
90
     */
91
    private function getPathname(Readable $file, string $inclusion): string
92
    {
93
        if (\in_array($inclusion[0], ['/', \DIRECTORY_SEPARATOR], true)) {
94
            return $inclusion;
95
        }
96
97
        $pathname = \dirname($file->getPathname()) . \DIRECTORY_SEPARATOR . $inclusion;
98
        $pathname = \str_replace('/./', '/', $pathname);
99
100
        return $pathname;
101
    }
102
}
103