Completed
Push — master ( 7efae6...e19db8 )
by Sebastian
03:17
created

Configurator::isPHPActionOptionValid()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 7
ccs 4
cts 4
cp 1
rs 9.4285
cc 2
eloc 4
nc 2
nop 1
crap 2
1
<?php
2
/**
3
 * This file is part of CaptainHook.
4
 *
5
 * (c) Sebastian Feldmann <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
namespace SebastianFeldmann\CaptainHook\Runner;
11
12
use SebastianFeldmann\CaptainHook\Config;
13
use SebastianFeldmann\CaptainHook\Console\IOUtil;
14
use SebastianFeldmann\CaptainHook\Hook\Util;
15
use SebastianFeldmann\CaptainHook\Runner;
16
use SebastianFeldmann\CaptainHook\Storage\File\Json;
17
18
/**
19
 * Class Configurator
20
 *
21
 * @package CaptainHook
22
 * @author  Sebastian Feldmann <[email protected]>
23
 * @link    https://github.com/sebastianfeldmann/captainhook
24
 * @since   Class available since Release 0.9.0
25
 */
26
class Configurator extends Runner
27
{
28
    /**
29
     * Force mode
30
     *
31
     * @var bool
32
     */
33
    private $force;
34
35
    /**
36
     * Extend existing config or create new one
37
     *
38
     * @var string
39
     */
40
    private $mode;
41
42
    /**
43
     * Execute the configurator.
44
     */
45 4
    public function run()
46
    {
47 4
        $config = $this->getConfigToManipulate();
48 3
        foreach (Util::getHooks() as $hook) {
49 3
            $this->configureHook($config, $hook);
50
        }
51 3
        $this->writeConfig($config);
52
53 3
        $this->io->write(
54
            [
55 3
                '<info>Configuration created successfully</info>',
56
                'Run <comment>\'vendor/bin/captainhook install\'</comment> to activate your hook configuration',
57
            ]
58
        );
59 3
    }
60
61
    /**
62
     * Force mode setter.
63
     *
64
     * @param  bool $force
65
     * @return \SebastianFeldmann\CaptainHook\Runner\Configurator
66
     */
67 2
    public function force(bool $force) : Configurator
68
    {
69 2
        $this->force = $force;
70 2
        return $this;
71
    }
72
73
    /**
74
     * Set configuration mode.
75
     *
76
     * @param  bool $extend
77
     * @return \SebastianFeldmann\CaptainHook\Runner\Configurator
78
     */
79 3
    public function extend(bool $extend) : Configurator
80
    {
81 3
        $this->mode = $extend ? 'extend' : 'create';
82 3
        return $this;
83
    }
84
85
    /**
86
     * Return config to handle.
87
     *
88
     * @return \SebastianFeldmann\CaptainHook\Config
89
     */
90 4
    public function getConfigToManipulate() : Config
91
    {
92
        // create mode, create blank configuration
93 4
        if ('extend' !== $this->mode) {
94
            // make sure the force option is set if the configuration file exists
95 2
            $this->ensureForce();
96 1
            return new Config($this->config->getPath());
97
        }
98 2
        return $this->config;
99
    }
100
101
    /**
102
     * Make sure force mode is set if config file exists.
103
     *
104
     * @throws \RuntimeException
105
     */
106 2
    private function ensureForce()
107
    {
108 2
        if ($this->config->isLoadedFromFile() && !$this->force) {
109 1
            throw new \RuntimeException('Configuration file exists, use -f to overwrite, or -e to extend');
110
        }
111 1
    }
112
113
    /**
114
     * Configure a hook.
115
     *
116
     * @param \SebastianFeldmann\CaptainHook\Config $config
117
     * @param string           $hook
118
     */
119 5
    public function configureHook(Config $config, string $hook)
120
    {
121 5
        $answer = $this->io->ask('  <info>Enable \'' . $hook . '\' hook?</info> <comment>[y,n]</comment> ', 'n');
122 5
        $enable = IOUtil::answerToBool($answer);
123
124
        /** @var \SebastianFeldmann\CaptainHook\Config\Hook $hookConfig */
125 5
        $hookConfig = $config->getHookConfig($hook);
126 5
        $hookConfig->setEnabled($enable);
127
128 5
        if ($enable) {
129 3
            $addAction = $this->io->ask('  <info>Add a validation action?</info> <comment>[y,n]</comment> ', 'n');
130
131 3
            while (IOUtil::answerToBool($addAction)) {
132 3
                $hookConfig->addAction($this->getActionConfig());
133
                // add another action?
134 3
                $addAction = $this->io->ask(
135 3
                    '  <info>Add another validation action?</info> <comment>[y,n]</comment> ',
136 3
                    'n'
137
                );
138
            }
139
        }
140 5
    }
141
142
    /**
143
     * Setup a action config with user input.
144
     *
145
     * @return \SebastianFeldmann\CaptainHook\Config\Action
146
     */
147 3
    public function getActionConfig() : Config\Action
148
    {
149 3
        $call    = $this->io->ask('  <info>PHP class or shell command to execute?</info> ', '');
150 3
        $type    = Util::getActionType($call);
151 3
        $options = $this->getActionOptions($type);
152
153 3
        return new Config\Action($type, $call, $options);
154
    }
155
156
    /**
157
     * Ask the user for any action options.
158
     *
159
     * @param  string $type
160
     * @return array
161
     */
162 3
    public function getActionOptions(string $type) : array
163
    {
164 3
        return 'php' === $type ? $this->getPHPActionOptions() : [];
165
    }
166
167
    /**
168
     * Get the php action options.
169
     *
170
     * @return array
171
     */
172 2
    protected function getPHPActionOptions() : array
173
    {
174 2
        $options = [];
175 2
        $addOption = $this->io->ask('  <info>Add a validator option?</info> <comment>[y,n]</comment> ', 'n');
176 2
        while (IOUtil::answerToBool($addOption)) {
177 2
            $options = array_merge($options, $this->getPHPActionOption());
178
            // add another action?
179 2
            $addOption = $this->io->ask('  <info>Add another validator option?</info> <comment>[y,n]</comment> ', 'n');
180
        }
181 2
        return $options;
182
    }
183
184
    /**
185
     * Ask the user for a php action option.
186
     *
187
     * @return array
188
     */
189 2
    protected function getPHPActionOption() : array
190
    {
191 2
        $result = [];
192 2
        $answer = $this->io->askAndValidate(
193 2
            '  <info>Specify options key and value</info> <comment>[key:value]</comment> ',
194 2
            ['\\SebastianFeldmann\\CaptainHook\\Runner\\Configurator', 'isPHPActionOptionValid'],
195 2
            3,
196 2
            null
197
        );
198 2
        if (null !== $answer) {
199 2
            list($key, $value) = explode(':', $answer);
200 2
            $result = [$key => $value];
201
        }
202 2
        return $result;
203
    }
204
205
    /**
206
     * Write config to project root.
207
     *
208
     * @param \SebastianFeldmann\CaptainHook\Config $config
209
     */
210 3
    public function writeConfig(Config $config)
211
    {
212 3
        $filePath = $this->config->getPath();
213 3
        $file     = new Json($filePath);
214 3
        $file->write($config->getJsonData());
215 3
    }
216
217
    /**
218
     * PHP action option validation.
219
     *
220
     * @param  $option
221
     * @return string
222
     * @throws \Exception
223
     */
224 2
    public static function isPHPActionOptionValid(string $option) : string
225
    {
226 2
        if (count(explode(':', $option)) !== 2) {
227 1
            throw new \Exception('Invalid option, use "key:value"');
228
        }
229 1
        return $option;
230
    }
231
}
232