99
1010class CommandExecutor
1111{
12- private const COMMAND_ALIASES = [
13- 'g ' => 'get ' ,
14- 's ' => 'set ' ,
15- 'd ' => 'delete ' ,
16- 'l ' => 'show ' ,
17- 'ls ' => 'show ' ,
18- ];
19-
20- private const NO_STAGE_COMMANDS = ['diff ' , 'copy ' , 'info ' , 'verify ' ];
21- private const NO_VAULT_COMMANDS = ['export ' , 'copy ' , 'info ' , 'verify ' ];
22- private const WRITE_COMMANDS = ['set ' , 'delete ' , 'copy ' , 'import ' , 'rename ' ];
2312
2413 public function __construct (
2514 private ShellContext $ context ,
@@ -39,16 +28,16 @@ public function execute(string $input): int
3928 $ parsed = $ this ->parseInput ($ input );
4029 $ commandName = $ parsed ['command ' ];
4130
42- // Handle export command specially with interactive flow
43- if ($ commandName === ' export ' ) {
44- return $ this ->runInteractiveExport ( $ parsed ['positionals ' ]);
31+ // Handle interactive commands (like export) with special flow
32+ if (CommandRegistry:: isInteractive ( $ commandName) ) {
33+ return $ this ->runInteractiveCommand ( $ commandName , $ parsed ['positionals ' ]);
4534 }
4635
4736 $ this ->validateCommand ($ commandName );
4837
4938 $ exitCode = $ this ->runCommand ($ commandName , $ parsed );
5039
51- if ($ this -> isWriteCommand ($ parsed ['command ' ])) {
40+ if (CommandRegistry:: isWriteCommand ($ parsed ['command ' ])) {
5241 $ this ->context ->invalidateCache ();
5342 }
5443
@@ -64,24 +53,12 @@ protected function parseInput(string $input): array
6453 $ parts = str_getcsv ($ input , ' ' , '" ' , '\\' );
6554 $ command = array_shift ($ parts );
6655
67- // In the interactive shell, we treat everything as positional arguments
68- // No -- prefix support for better UX
69- $ positionals = $ parts ;
70- $ options = [];
71-
7256 return [
73- 'command ' => $ this ->mapAlias ($ command ),
74- 'positionals ' => $ positionals ,
75- 'options ' => $ options ,
57+ 'command ' => CommandRegistry::resolveAlias ($ command ),
58+ 'positionals ' => $ parts ,
7659 ];
7760 }
7861
79-
80- protected function mapAlias (string $ command ): string
81- {
82- return self ::COMMAND_ALIASES [$ command ] ?? $ command ;
83- }
84-
8562 protected function validateCommand (string $ commandName ): void
8663 {
8764 if (!$ this ->application ->has ($ commandName )) {
@@ -108,126 +85,28 @@ protected function buildCommandInput(string $commandName, array $parsed): array
10885 {
10986 $ input = ['command ' => $ commandName ];
11087
111- // Process positionals and options based on command
112- $ this ->processPositionalArguments (
113- $ parsed ['command ' ],
114- $ parsed ['positionals ' ],
115- $ input
116- );
88+ // Use the new ArgumentProcessor for clean argument handling
89+ ArgumentProcessor::process ($ parsed ['command ' ], $ parsed ['positionals ' ], $ input );
11790
118- $ this ->addOptions ($ parsed ['options ' ], $ input );
119-
120- $ this ->addContextIfNeeded ($ parsed ['command ' ], $ parsed ['options ' ], $ input );
91+ // Add context (vault/stage) if needed
92+ $ this ->addContextIfNeeded ($ parsed ['command ' ], $ input );
12193
12294 return $ input ;
12395 }
12496
125- protected function processPositionalArguments (string $ command , array $ positionals , array &$ input ): void
126- {
127- switch ($ command ) {
128- case 'set ' :
129- $ this ->addIfExists ($ positionals , 0 , 'key ' , $ input );
130- $ this ->addIfExists ($ positionals , 1 , 'value ' , $ input );
131- break ;
132-
133- case 'get ' :
134- case 'history ' :
135- // Just take the key, ignore any format options (shell is for humans)
136- $ this ->addIfExists ($ positionals , 0 , 'key ' , $ input );
137- break ;
138-
139- case 'export ' :
140- // Export is handled by InteractiveExport, this shouldn't be reached
141- break ;
142-
143- case 'copy ' :
144- $ this ->addIfExists ($ positionals , 0 , 'key ' , $ input );
145- // Handle optional destination argument
146- if (isset ($ positionals [1 ])) {
147- $ input ['--to ' ] = $ positionals [1 ];
148- }
149- break ;
150-
151- case 'import ' :
152- $ this ->addIfExists ($ positionals , 0 , 'file ' , $ input );
153- break ;
154-
155- case 'stage:add ' :
156- $ this ->addIfExists ($ positionals , 0 , 'name ' , $ input );
157- break ;
158-
159- case 'show ' :
160- // Only support 'unmask' in interactive shell (no format options)
161- if (in_array ('unmask ' , $ positionals )) {
162- $ input ['--unmask ' ] = true ;
163- }
164- // Don't pass any positionals to show command
165- $ positionals = [];
166- break ;
167-
168- case 'delete ' :
169- // Handle "delete KEY force" without -- prefix
170- foreach ($ positionals as $ index => $ arg ) {
171- if ($ arg === 'force ' && $ index > 0 ) {
172- $ input ['--force ' ] = true ;
173- unset($ positionals [$ index ]);
174- }
175- }
176- // First positional is the key
177- $ this ->addIfExists (array_values ($ positionals ), 0 , 'key ' , $ input );
178- break ;
179-
180- case 'rename ' :
181- // Handle "rename OLD NEW [force]" without -- prefix
182- $ this ->addIfExists ($ positionals , 0 , 'old ' , $ input );
183- $ this ->addIfExists ($ positionals , 1 , 'new ' , $ input );
184- if (in_array ('force ' , $ positionals )) {
185- $ input ['--force ' ] = true ;
186- }
187- break ;
188-
189- case 'search ' :
190- // Handle "search QUERY [unmask] [case-sensitive]" without -- prefix
191- $ this ->addIfExists ($ positionals , 0 , 'query ' , $ input );
192- if (in_array ('unmask ' , $ positionals )) {
193- $ input ['--unmask ' ] = true ;
194- }
195- if (in_array ('case-sensitive ' , $ positionals )) {
196- $ input ['--case-sensitive ' ] = true ;
197- }
198- break ;
199- }
200- }
201-
202- protected function addIfExists (array $ array , int $ index , string $ key , array &$ target ): void
203- {
204- if (isset ($ array [$ index ])) {
205- $ target [$ key ] = $ array [$ index ];
206- }
207- }
20897
209- protected function addOptions ( array $ options , array &$ input ): void
98+ protected function addContextIfNeeded ( string $ command , array &$ input ): void
21099 {
211- foreach ($ options as $ key => $ value ) {
212- $ input ['-- ' . $ key ] = $ value ;
213- }
214- }
215-
216- protected function addContextIfNeeded (string $ command , array $ options , array &$ input ): void
217- {
218- $ needsStage = !in_array ($ command , self ::NO_STAGE_COMMANDS );
219- $ needsVault = !in_array ($ command , self ::NO_VAULT_COMMANDS );
220-
221- if ($ needsStage && !isset ($ options ['stage ' ])) {
100+ if (CommandRegistry::requiresStage ($ command ) && !isset ($ input ['--stage ' ])) {
222101 $ input ['--stage ' ] = $ this ->context ->getStage ();
223102 }
224103
225- if ($ needsVault && !isset ($ options [ ' vault ' ])) {
104+ if (CommandRegistry:: requiresVault ( $ command ) && !isset ($ input [ ' -- vault ' ])) {
226105 $ input ['--vault ' ] = $ this ->context ->getVault ();
227106 }
228107
229- // Special handling for copy command
230- if ($ command === 'copy ' && !isset ($ options [ ' from ' ])) {
108+ // Special handling for copy command's 'from' context
109+ if ($ command === 'copy ' && !isset ($ input [ ' -- from ' ])) {
231110 $ input ['--from ' ] = sprintf (
232111 "%s:%s " ,
233112 $ this ->context ->getVault (),
@@ -236,23 +115,21 @@ protected function addContextIfNeeded(string $command, array $options, array &$i
236115 }
237116 }
238117
239- protected function isWriteCommand (string $ command ): bool
240- {
241- return in_array ($ command , self ::WRITE_COMMANDS );
242- }
243-
244118 protected function outputError (string $ message ): void
245119 {
246120 $ output = new ConsoleOutput ();
247121 $ output ->writeln ("<error> {$ message }</error> " );
248122 }
249123
250124 /**
251- * Run the interactive export command
125+ * Run an interactive command (like export)
252126 */
253- protected function runInteractiveExport ( array $ args ): int
127+ protected function runInteractiveCommand ( string $ command , array $ args ): int
254128 {
255- $ interactiveExport = new InteractiveExport ($ this ->context , $ this ->application );
256- return $ interactiveExport ->execute ($ args );
129+ // Currently only export is interactive, but this is extensible
130+ return match ($ command ) {
131+ 'export ' => (new InteractiveExport ($ this ->context , $ this ->application ))->execute ($ args ),
132+ default => throw new \Exception ("Unknown interactive command: {$ command }" )
133+ };
257134 }
258135}
0 commit comments