OXYGENHUB
◆ main openmsx / src / commands / Command.hh
3.9 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#ifndef COMMAND_HH
2
#define COMMAND_HH
3
4
#include "CommandException.hh"
5
#include "Completer.hh"
6
7
#include "strCat.hh"
8
9
#include <cassert>
10
#include <span>
11
#include <string_view>
12
#include <vector>
13
14
namespace openmsx {
15
16
class CommandController;
17
class GlobalCommandController;
18
class Interpreter;
19
class TclObject;
20
class CliComm;
21
22
class CommandCompleter : public Completer
23
{
24
public:
25
	CommandCompleter(const CommandCompleter&) = delete;
26
	CommandCompleter(CommandCompleter&&) = delete;
27
	CommandCompleter& operator=(const CommandCompleter&) = delete;
28
	CommandCompleter& operator=(CommandCompleter&&) = delete;
29
30
	[[nodiscard]] CommandController& getCommandController() const { return commandController; }
31
	[[nodiscard]] Interpreter& getInterpreter() const final;
32
33
protected:
34
	CommandCompleter(CommandController& controller, std::string_view name);
35
	~CommandCompleter();
36
37
	[[nodiscard]] GlobalCommandController& getGlobalCommandController() const;
38
	[[nodiscard]] CliComm& getCliComm() const;
39
40
private:
41
	CommandController& commandController;
42
};
43
44
45
class Command : public CommandCompleter
46
{
47
	struct UnknownSubCommand {};
48
49
public:
50
	/** Execute this command.
51
	  * @param tokens Tokenized command line;
52
	  *     tokens[0] is the command itself.
53
	  * @param result The result of the command must be assigned to this
54
	  *               parameter.
55
	  * @throws CommandException Thrown when there was an error while
56
	  *                          executing this command.
57
	  */
58
	virtual void execute(std::span<const TclObject> tokens, TclObject& result) = 0;
59
60
	/** Attempt tab completion for this command.
61
	  * Default implementation does nothing.
62
	  * @param tokens Tokenized command line;
63
	  *     tokens[0] is the command itself.
64
	  *     The last token is incomplete, this method tries to complete it.
65
	  */
66
	void tabCompletion(std::vector<std::string>& tokens) const override;
67
68
	// see comments in MSXMotherBoard::loadMachineCommand
69
	void setAllowedInEmptyMachine(bool value) { allowInEmptyMachine = value; }
70
	[[nodiscard]] bool isAllowedInEmptyMachine() const { return allowInEmptyMachine; }
71
72
	// used by Interpreter::(un)registerCommand()
73
	void setToken(void* token_) { assert(!token); token = token_; }
74
	[[nodiscard]] void* getToken() const { return token; }
75
76
	// helper to delegate to a subcommand
77
	template<typename... Args>
78
	void executeSubCommand(std::string_view subCmd, Args&&... args) {
79
		try {
80
			executeSubCommandImpl(subCmd, std::forward<Args>(args)...);
81
		} catch (UnknownSubCommand&) {
82
			unknownSubCommand(subCmd, std::forward<Args>(args)...);
83
		}
84
	}
85
86
protected:
87
	Command(CommandController& controller, std::string_view name);
88
	~Command();
89
90
private:
91
	template<typename Func, typename... Args>
92
	void executeSubCommandImpl(std::string_view subCmd, std::string_view candidate, Func func, Args&&... args) const {
93
		if (subCmd == candidate) {
94
			func();
95
		} else {
96
			executeSubCommandImpl(subCmd, std::forward<Args>(args)...);
97
		}
98
	}
99
	[[noreturn]] void executeSubCommandImpl(std::string_view /*subCmd*/) const {
100
		throw UnknownSubCommand{}; // exhausted all possible candidates
101
	}
102
103
	template<typename Func, typename... Args>
104
	void unknownSubCommand(std::string_view subCmd, std::string_view firstCandidate, Func /*func*/, Args&&... args) const {
105
		unknownSubCommandImpl(strCat("Unknown subcommand '", subCmd, "'. Must be one of '", firstCandidate, '\''),
106
		                      std::forward<Args>(args)...);
107
	}
108
	template<typename Func, typename... Args>
109
	void unknownSubCommandImpl(std::string message, std::string_view candidate, Func /*func*/, Args&&... args) const {
110
		strAppend(message, ", '", candidate, '\'');
111
		unknownSubCommandImpl(message, std::forward<Args>(args)...);
112
		throw SyntaxError();
113
	}
114
	template<typename Func>
115
	void unknownSubCommandImpl(std::string message, std::string_view lastCandidate, Func /*func*/) const {
116
		strAppend(message, " or '", lastCandidate, "'.");
117
		throw CommandException(message);
118
	}
119
120
private:
121
	bool allowInEmptyMachine = true;
122
	void* token = nullptr;
123
};
124
125
} // namespace openmsx
126
127
#endif