BuildTokenMatcherCommand   A
last analyzed

Complexity

Total Complexity 12

Size/Duplication

Total Lines 105
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 41
dl 0
loc 105
rs 10
c 0
b 0
f 0
wmc 12

8 Methods

Rating   Name   Duplication   Size   Complexity  
A getFileComments() 0 9 2
A configure() 0 8 1
A buildTarget() 0 10 2
A getTargetFile() 0 3 1
A buildContent() 0 11 2
A getSpecFile() 0 9 2
A buildSpec() 0 8 1
A execute() 0 8 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Remorhaz\UniLex\Console;
6
7
use ReflectionException;
8
use Remorhaz\UniLex\Exception;
9
use Remorhaz\UniLex\Lexer\TokenMatcherGenerator;
10
use Remorhaz\UniLex\Lexer\TokenMatcherSpec;
11
use Remorhaz\UniLex\Lexer\TokenMatcherSpecParser;
12
use RuntimeException;
13
use Symfony\Component\Console\Command\Command;
14
use Symfony\Component\Console\Exception\InvalidOptionException;
15
use Symfony\Component\Console\Input\InputArgument;
16
use Symfony\Component\Console\Input\InputInterface;
17
use Symfony\Component\Console\Input\InputOption;
18
use Symfony\Component\Console\Output\OutputInterface;
19
20
use function array_merge;
21
use function file_put_contents;
22
use function realpath;
23
use function strlen;
24
use function substr_count;
25
26
final class BuildTokenMatcherCommand extends Command
27
{
28
    protected static $defaultName = 'build-token-matcher';
29
30
    protected function configure()
31
    {
32
        $this
33
            ->setDescription('Generates token matcher.')
34
            ->setHelp('Generates token matcher based on given specification.')
35
            ->addArgument('spec', InputArgument::REQUIRED, 'Specification file')
36
            ->addArgument('target', InputArgument::REQUIRED, 'Target file')
37
            ->addOption('desc', 'd', InputOption::VALUE_REQUIRED, 'Token matcher description');
38
    }
39
40
    /**
41
     * @param InputInterface  $input
42
     * @param OutputInterface $output
43
     * @return int|void
44
     * @throws ReflectionException
45
     * @throws Exception
46
     */
47
    protected function execute(InputInterface $input, OutputInterface $output)
48
    {
49
        $output->writeln("Building token matcher...");
50
        $spec = $this->buildSpec($input, $output);
51
        $content = $this->buildContent($output, $spec);
52
        $this->buildTarget($input, $output, $content);
53
54
        return 0;
55
    }
56
57
    /**
58
     * @param InputInterface  $input
59
     * @param OutputInterface $output
60
     * @return TokenMatcherSpec
61
     * @throws Exception
62
     * @throws ReflectionException
63
     */
64
    private function buildSpec(InputInterface $input, OutputInterface $output): TokenMatcherSpec
65
    {
66
        $specFile = $this->getSpecFile($input);
67
        $output->writeln("Specification used: {$specFile}");
68
69
        return TokenMatcherSpecParser::loadFromFile($specFile)
70
            ->getMatcherSpec()
71
            ->addFileComment(...$this->getFileComments($input));
72
    }
73
74
    /**
75
     * @param OutputInterface  $output
76
     * @param TokenMatcherSpec $spec
77
     * @return string
78
     * @throws Exception
79
     * @throws ReflectionException
80
     */
81
    private function buildContent(OutputInterface $output, TokenMatcherSpec $spec): string
82
    {
83
        $generator = new TokenMatcherGenerator($spec);
84
        $content = $generator->getOutput();
85
        $byteCount = strlen($content);
86
        $lineCount = $byteCount > 0
87
            ? substr_count($content, "\n") + 1
88
            : 0;
89
        $output->writeln("Done ({$lineCount} lines)!");
90
91
        return $content;
92
    }
93
94
    private function buildTarget(InputInterface $input, OutputInterface $output, string $content): void
95
    {
96
        $targetFile = $this->getTargetFile($input);
97
        $output->writeln("Saving generated data to file {$targetFile}...");
98
99
        if (false === file_put_contents($targetFile, $content)) {
100
            throw new RuntimeException("Failed to write file {$targetFile}");
101
        }
102
        $byteCount = strlen($content);
103
        $output->writeln("Done ({$byteCount} bytes)!");
104
    }
105
106
    private function getTargetFile(InputInterface $input): string
107
    {
108
        return $input->getArgument('target');
109
    }
110
111
    private function getSpecFile(InputInterface $input): string
112
    {
113
        $specFile = $input->getArgument('spec');
114
        $specFile = realpath($specFile);
115
        if (false === $specFile) {
116
            throw new InvalidOptionException("Argument #1 must contain valid path to specification file");
117
        }
118
119
        return $specFile;
120
    }
121
122
    private function getFileComments(InputInterface $input): array
123
    {
124
        $description = $input->getOption('desc');
125
126
        return array_merge(
127
            isset($description) ? [$description, ''] : [],
128
            [
129
                "Auto-generated file, please don't edit manually.",
130
                "Generated by UniLex.",
131
            ]
132
        );
133
    }
134
}
135