VST3: fetch midi mappings all at once, use it for note/sound-off
[carla.git] / source / modules / juce_core / misc / juce_ConsoleApplication.cpp
blob7b6e172a81803b10b05e1e0264ab2037db53e6f6
1 /*
2 ==============================================================================
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
10 The code included in this file is provided under the terms of the ISC license
11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12 To use, copy, modify, and/or distribute this software for any purpose with or
13 without fee is hereby granted provided that the above copyright notice and
14 this permission notice appear in all copies.
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18 DISCLAIMED.
20 ==============================================================================
23 namespace juce
26 static File resolveFilename (const String& name)
28 return File::getCurrentWorkingDirectory().getChildFile (name.unquoted());
31 static File checkFileExists (const File& f)
33 if (! f.exists())
34 ConsoleApplication::fail ("Could not find file: " + f.getFullPathName());
36 return f;
39 static File checkFolderExists (const File& f)
41 if (! f.isDirectory())
42 ConsoleApplication::fail ("Could not find folder: " + f.getFullPathName());
44 return f;
47 static File resolveFilenameForOption (const ArgumentList& args, StringRef option, const String& filename)
49 if (filename.isEmpty())
51 args.failIfOptionIsMissing (option);
52 ConsoleApplication::fail ("Expected a filename after the " + option + " option");
55 return resolveFilename (filename);
58 File ArgumentList::Argument::resolveAsFile() const
60 return resolveFilename (text);
63 File ArgumentList::Argument::resolveAsExistingFile() const
65 return checkFileExists (resolveAsFile());
68 File ArgumentList::Argument::resolveAsExistingFolder() const
70 auto f = resolveAsFile();
72 if (! f.isDirectory())
73 ConsoleApplication::fail ("Could not find folder: " + f.getFullPathName());
75 return f;
78 static bool isShortOptionFormat (StringRef s) { return s[0] == '-' && s[1] != '-'; }
79 static bool isLongOptionFormat (StringRef s) { return s[0] == '-' && s[1] == '-' && s[2] != '-'; }
80 static bool isOptionFormat (StringRef s) { return s[0] == '-'; }
82 bool ArgumentList::Argument::isLongOption() const { return isLongOptionFormat (text); }
83 bool ArgumentList::Argument::isShortOption() const { return isShortOptionFormat (text); }
84 bool ArgumentList::Argument::isOption() const { return isOptionFormat (text); }
86 bool ArgumentList::Argument::isLongOption (const String& option) const
88 if (! isLongOptionFormat (option))
90 jassert (! isShortOptionFormat (option)); // this will always fail to match
91 return isLongOption ("--" + option);
94 return text.upToFirstOccurrenceOf ("=", false, false) == option;
97 String ArgumentList::Argument::getLongOptionValue() const
99 if (isLongOption())
101 auto equalsIndex = text.indexOfChar ('=');
103 if (equalsIndex > 0)
104 return text.substring (equalsIndex + 1);
107 return {};
110 bool ArgumentList::Argument::isShortOption (char option) const
112 jassert (option != '-'); // this is probably not what you intended to pass in
114 return isShortOption() && text.containsChar (String (option)[0]);
117 bool ArgumentList::Argument::operator== (StringRef wildcard) const
119 for (auto& o : StringArray::fromTokens (wildcard, "|", {}))
121 if (text == o)
122 return true;
124 if (isShortOptionFormat (o) && o.length() == 2 && isShortOption ((char) o[1]))
125 return true;
127 if (isLongOptionFormat (o) && isLongOption (o))
128 return true;
131 return false;
134 bool ArgumentList::Argument::operator!= (StringRef s) const { return ! operator== (s); }
136 //==============================================================================
137 ArgumentList::ArgumentList (String exeName, StringArray args)
138 : executableName (std::move (exeName))
140 args.trim();
141 args.removeEmptyStrings();
143 for (auto& a : args)
144 arguments.add ({ a.unquoted() });
147 ArgumentList::ArgumentList (int argc, char* argv[])
148 : ArgumentList (argv[0], StringArray (argv + 1, argc - 1))
152 ArgumentList::ArgumentList (const String& exeName, const String& args)
153 : ArgumentList (exeName, StringArray::fromTokens (args, true))
157 int ArgumentList::size() const { return arguments.size(); }
158 ArgumentList::Argument ArgumentList::operator[] (int index) const { return arguments[index]; }
160 void ArgumentList::checkMinNumArguments (int expectedMinNumberOfArgs) const
162 if (size() < expectedMinNumberOfArgs)
163 ConsoleApplication::fail ("Not enough arguments!");
166 int ArgumentList::indexOfOption (StringRef option) const
168 jassert (option == String (option).trim()); // passing non-trimmed strings will always fail to find a match!
170 for (int i = 0; i < arguments.size(); ++i)
171 if (arguments.getReference (i) == option)
172 return i;
174 return -1;
177 bool ArgumentList::containsOption (StringRef option) const
179 return indexOfOption (option) >= 0;
182 bool ArgumentList::removeOptionIfFound (StringRef option)
184 auto i = indexOfOption (option);
186 if (i >= 0)
187 arguments.remove (i);
189 return i >= 0;
192 void ArgumentList::failIfOptionIsMissing (StringRef option) const
194 if (indexOfOption (option) < 0)
195 ConsoleApplication::fail ("Expected the option " + option);
198 String ArgumentList::getValueForOption (StringRef option) const
200 jassert (isOptionFormat (option)); // the thing you're searching for must be an option
202 for (int i = 0; i < arguments.size(); ++i)
204 auto& arg = arguments.getReference(i);
206 if (arg == option)
208 if (arg.isShortOption())
210 if (i < arguments.size() - 1 && ! arguments.getReference (i + 1).isOption())
211 return arguments.getReference (i + 1).text;
213 return {};
216 if (arg.isLongOption())
217 return arg.getLongOptionValue();
221 return {};
224 String ArgumentList::removeValueForOption (StringRef option)
226 jassert (isOptionFormat (option)); // the thing you're searching for must be an option
228 for (int i = 0; i < arguments.size(); ++i)
230 auto& arg = arguments.getReference(i);
232 if (arg == option)
234 if (arg.isShortOption())
236 if (i < arguments.size() - 1 && ! arguments.getReference (i + 1).isOption())
238 auto result = arguments.getReference (i + 1).text;
239 arguments.removeRange (i, 2);
240 return result;
243 arguments.remove (i);
244 return {};
247 if (arg.isLongOption())
249 auto result = arg.getLongOptionValue();
250 arguments.remove (i);
251 return result;
256 return {};
259 File ArgumentList::getFileForOption (StringRef option) const
261 return resolveFilenameForOption (*this, option, getValueForOption (option));
264 File ArgumentList::getFileForOptionAndRemove (StringRef option)
266 return resolveFilenameForOption (*this, option, removeValueForOption (option));
269 File ArgumentList::getExistingFileForOption (StringRef option) const
271 return checkFileExists (getFileForOption (option));
274 File ArgumentList::getExistingFileForOptionAndRemove (StringRef option)
276 return checkFileExists (getFileForOptionAndRemove (option));
279 File ArgumentList::getExistingFolderForOption (StringRef option) const
281 return checkFolderExists (getFileForOption (option));
284 File ArgumentList::getExistingFolderForOptionAndRemove (StringRef option)
286 return checkFolderExists (getFileForOptionAndRemove (option));
289 //==============================================================================
290 struct ConsoleAppFailureCode
292 String errorMessage;
293 int returnCode;
296 void ConsoleApplication::fail (String errorMessage, int returnCode)
298 throw ConsoleAppFailureCode { std::move (errorMessage), returnCode };
301 int ConsoleApplication::invokeCatchingFailures (std::function<int()>&& f)
303 int returnCode = 0;
307 returnCode = f();
309 catch (const ConsoleAppFailureCode& error)
311 std::cerr << error.errorMessage << std::endl;
312 returnCode = error.returnCode;
315 return returnCode;
318 const ConsoleApplication::Command* ConsoleApplication::findCommand (const ArgumentList& args, bool optionMustBeFirstArg) const
320 for (auto& c : commands)
322 auto index = args.indexOfOption (c.commandOption);
324 if (optionMustBeFirstArg ? (index == 0) : (index >= 0))
325 return &c;
328 if (commandIfNoOthersRecognised >= 0)
329 return &commands[(size_t) commandIfNoOthersRecognised];
331 return {};
334 int ConsoleApplication::findAndRunCommand (const ArgumentList& args, bool optionMustBeFirstArg) const
336 return invokeCatchingFailures ([&args, optionMustBeFirstArg, this]
338 if (auto c = findCommand (args, optionMustBeFirstArg))
339 c->command (args);
340 else
341 fail ("Unrecognised arguments");
343 return 0;
347 int ConsoleApplication::findAndRunCommand (int argc, char* argv[]) const
349 return findAndRunCommand (ArgumentList (argc, argv));
352 void ConsoleApplication::addCommand (Command c)
354 commands.emplace_back (std::move (c));
357 void ConsoleApplication::addDefaultCommand (Command c)
359 commandIfNoOthersRecognised = (int) commands.size();
360 addCommand (std::move (c));
363 void ConsoleApplication::addHelpCommand (String arg, String helpMessage, bool makeDefaultCommand)
365 Command c { arg, arg, "Prints the list of commands", {},
366 [this, helpMessage] (const ArgumentList& args)
368 std::cout << helpMessage << std::endl;
369 printCommandList (args);
372 if (makeDefaultCommand)
373 addDefaultCommand (std::move (c));
374 else
375 addCommand (std::move (c));
378 void ConsoleApplication::addVersionCommand (String arg, String versionText)
380 addCommand ({ arg, arg, "Prints the current version number", {},
381 [versionText] (const ArgumentList&)
383 std::cout << versionText << std::endl;
384 }});
387 const std::vector<ConsoleApplication::Command>& ConsoleApplication::getCommands() const
389 return commands;
392 static String getExeNameAndArgs (const ArgumentList& args, const ConsoleApplication::Command& command)
394 auto exeName = args.executableName.fromLastOccurrenceOf ("/", false, false)
395 .fromLastOccurrenceOf ("\\", false, false);
397 return " " + exeName + " " + command.argumentDescription;
400 static void printCommandDescription (const ArgumentList& args, const ConsoleApplication::Command& command,
401 int descriptionIndent)
403 auto nameAndArgs = getExeNameAndArgs (args, command);
405 if (nameAndArgs.length() > descriptionIndent)
406 std::cout << nameAndArgs << std::endl << String().paddedRight (' ', descriptionIndent);
407 else
408 std::cout << nameAndArgs.paddedRight (' ', descriptionIndent);
410 std::cout << command.shortDescription << std::endl;
413 void ConsoleApplication::printCommandList (const ArgumentList& args) const
415 int descriptionIndent = 0;
417 for (auto& c : commands)
418 descriptionIndent = std::max (descriptionIndent, getExeNameAndArgs (args, c).length());
420 descriptionIndent = std::min (descriptionIndent + 2, 40);
422 for (auto& c : commands)
423 printCommandDescription (args, c, descriptionIndent);
425 std::cout << std::endl;
428 void ConsoleApplication::printCommandDetails (const ArgumentList& args, const Command& command) const
430 auto len = getExeNameAndArgs (args, command).length();
432 printCommandDescription (args, command, std::min (len + 3, 40));
434 if (command.longDescription.isNotEmpty())
435 std::cout << std::endl << command.longDescription << std::endl;
439 } // namespace juce