Branch data Line data Source code
1 : : #pragma once
2 : :
3 : : /*
4 : : MIT License
5 : :
6 : : Copyright (c) 2014-2024 Stephane Cuillerdier (aka aiekick)
7 : :
8 : : Permission is hereby granted, free of charge, to any person obtaining a copy
9 : : of this software and associated documentation files (the "Software"), to deal
10 : : in the Software without restriction, including without limitation the rights
11 : : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 : : copies of the Software, and to permit persons to whom the Software is
13 : : furnished to do so, subject to the following conditions:
14 : :
15 : : The above copyright notice and this permission notice shall be included in all
16 : : copies or substantial portions of the Software.
17 : :
18 : : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 : : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 : : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 : : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 : : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 : : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 : : SOFTWARE.
25 : : */
26 : :
27 : : // ezCmdProc is part of the ezLibs project : https://github.com/aiekick/ezLibs.git
28 : :
29 : : #include <string>
30 : : #include <vector>
31 : : #include <functional>
32 : : #include <unordered_map>
33 : :
34 : : namespace ez {
35 : : /*
36 : : this class will help process commands like serialize/deserialize operations
37 : : usually for :
38 : : - server/client communication
39 : : - undo/redo operation
40 : : - ..
41 : : */
42 : :
43 : : class CmdProcessor {
44 : : public:
45 : : typedef std::string Command;
46 : : typedef std::string ProcessedCommand;
47 : : typedef std::vector<std::string> Arguments;
48 : : typedef std::function<void(const Command &, const Arguments &)> CmdFunctor;
49 : :
50 : : private:
51 : : std::unordered_map<Command, CmdFunctor> m_cmdFunctors;
52 : :
53 : : /* a processed commande will have this format : (space are not part of the process command, jsut for syntax clarity)
54 : : * COMMAND(DELIMITER ARG1 DELIMITER ARG2 DELIMITER ARG3)
55 : : *
56 : : * if not argument are sent :
57 : : * COMMAND()
58 : : *
59 : : * if one argument is sent :
60 : : * COMMAND(ARG1)
61 : : */
62 : :
63 : : public:
64 : : /// <summary>
65 : : /// will encode a command ready to send
66 : : /// the encoding will fail if the delimiter exist in args words
67 : : /// </summary>
68 : : /// <param name="vCmd">the command</param>
69 : : /// <param name="vArgs">the command arguments</param>
70 : : /// <returns>the processed command</returns>
71 : 6 : ProcessedCommand encode(const Command &vCmd, Arguments vArgs, char vDelimiter = ';') {
72 : 6 : ProcessedCommand ret;
73 [ + - ]: 6 : if (!vCmd.empty()) {
74 [ + + ]: 6 : if (vArgs.empty()) {
75 : : // no args
76 : 1 : ret = vCmd + "()";
77 : 5 : } else {
78 : 5 : ret = vCmd + "(";
79 : 5 : bool first = true;
80 [ + + ]: 5 : if (vArgs.size() == 1U) {
81 : : // one args
82 : 1 : ret += *vArgs.begin();
83 : 4 : } else {
84 [ + + ]: 12 : for (const auto &arg: vArgs) {
85 : : // many args
86 [ + + ]: 12 : if (arg.find(vDelimiter) != std::string::npos) {
87 : : // found
88 : 1 : return {};
89 : 1 : }
90 : 11 : ret += vDelimiter + arg;
91 : 11 : first = false;
92 : 11 : }
93 : 4 : }
94 : 4 : ret += ")";
95 : 4 : }
96 : 6 : } else {
97 : : #ifdef EZ_TOOLS_LOG
98 : : LogVarError("Err : the cmd is empty");
99 : : #endif // EZ_TOOLS_LOG
100 : 0 : }
101 : 5 : return ret;
102 : 6 : }
103 : :
104 : : /// <summary>
105 : : /// will decode a command and call the registered functor
106 : : /// </summary>
107 : : /// <param name="vCmd"></param>
108 : : /// <returns>true is sucessfully decoded</returns>
109 : 10 : bool decode(const ProcessedCommand &vCmd) {
110 : 10 : bool ret = false;
111 [ + - ]: 10 : if (!vCmd.empty()) {
112 : 10 : auto first_par = vCmd.find('(');
113 [ + - ]: 10 : if (first_par != std::string::npos) {
114 : 10 : const Command cmd = vCmd.substr(0, first_par);
115 : 10 : const auto end_par = vCmd.find(')', first_par);
116 [ + - ]: 10 : if (end_par != std::string::npos) {
117 [ + + ]: 10 : if (m_cmdFunctors.find(cmd) != m_cmdFunctors.end()) {
118 : 5 : auto functor = m_cmdFunctors.at(cmd);
119 [ + - ]: 5 : if (functor != nullptr) {
120 : : // normally impossible to have null functor
121 : 5 : ret = true;
122 : 5 : ++first_par;
123 : 5 : Arguments args;
124 [ + + ]: 5 : if (first_par != end_par) {
125 : 4 : char delimiter = vCmd.at(first_par);
126 : 4 : auto next_sep = vCmd.find(delimiter, first_par + 1);
127 [ + + ]: 4 : if (next_sep == std::string::npos) {
128 : : // one arg
129 : 1 : args.push_back(vCmd.substr(first_par, end_par - first_par));
130 : 3 : } else {
131 : : // many args
132 : 3 : size_t last_sep = first_par + 1; // we go after the delimiter
133 : 8 : do {
134 : 8 : args.push_back(vCmd.substr(last_sep, next_sep - last_sep));
135 : 8 : last_sep = next_sep + 1;
136 [ + + ]: 8 : } while ((next_sep = vCmd.find(delimiter, last_sep)) != std::string::npos);
137 : 3 : args.push_back(vCmd.substr(last_sep, end_par - last_sep));
138 : 3 : }
139 : 4 : }
140 : : // call functor
141 : 5 : functor(cmd, args);
142 : 5 : }
143 : 5 : }
144 : 10 : }
145 : 10 : }
146 : 10 : }
147 : 10 : return ret;
148 : 10 : }
149 : :
150 : : /// <summary>
151 : : /// will check is a commande is registered. si a functor is available for this command
152 : : /// </summary>
153 : : /// <param name="vCmd">the command to check</param>
154 : : /// <returns>true if registered</returns>
155 : 17 : bool isCmdRegistered(const Command &vCmd) { return (m_cmdFunctors.find(vCmd) != m_cmdFunctors.end()); }
156 : :
157 : : /// <summary>
158 : : /// will register a functor for a command
159 : : /// </summary>
160 : : /// <param name="vCmd">the commande to register</param>
161 : : /// <returns>true if successfully registered. false if command is empty or functor is null</returns>
162 : 7 : bool registerCmd(const Command &vCmd, const CmdFunctor &vFunctor) {
163 : 7 : bool ret = false;
164 [ + + ][ + + ]: 7 : if (!vCmd.empty() && vFunctor != nullptr) {
165 [ + - ]: 5 : if (m_cmdFunctors.find(vCmd) == m_cmdFunctors.end()) {
166 : : // not found
167 : 5 : m_cmdFunctors[vCmd] = vFunctor;
168 : 5 : ret = true;
169 : 5 : }
170 : 5 : }
171 : 7 : return ret;
172 : 7 : }
173 : :
174 : : /// <summary>
175 : : /// will unregister the command. the functor will be removed for this command
176 : : /// </summary>
177 : : /// <param name="vCmd">the commande to unregister</param>
178 : : /// <returns>true is successfully unregistered; false if the commande was not registered before</returns>
179 : 5 : bool unRegisterCmd(const Command &vCmd) {
180 : 5 : bool ret = false;
181 [ + - ]: 5 : if (m_cmdFunctors.find(vCmd) != m_cmdFunctors.end()) {
182 : : // not found
183 : 5 : m_cmdFunctors.erase(vCmd);
184 : 5 : ret = true;
185 : 5 : }
186 : 5 : return ret;
187 : 5 : }
188 : : };
189 : : } // namespace ez
|