1/*
2 * Copyright (c) 2011-2014, Intel Corporation
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation and/or
13 * other materials provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its contributors
16 * may be used to endorse or promote products derived from this software without
17 * specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
23 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30#pragma once
31
32#include <vector>
33#include "RemoteCommandHandler.h"
34
35template <class CCommandParser>
36class TRemoteCommandHandlerTemplate : public IRemoteCommandHandler
37{
38public:
39    /** Remote command parser execution return status */
40    enum CommandStatus
41    {
42        EDone,      /**< Command succeded, return "Done" */
43        ESucceeded, /**< Command succeeded */
44        EFailed,    /**< Command failed */
45        EShowUsage  /**< Command failed, show usage */
46    };
47
48    /** Type of the remote command callbacks
49     *
50     * @param[in] remoteCommand contains the arguments of the received command.
51     * @param[out] strResult a string containing the result of the command.
52     *
53     * @return the command execution status, @see CommandStatus
54     */
55    typedef CommandStatus (CCommandParser::*RemoteCommandParser)(
56        const IRemoteCommand &remoteCommand, std::string &strResult);
57
58private:
59    // Parser descriptions
60    class CRemoteCommandParserItem
61    {
62    public:
63        CRemoteCommandParserItem(const std::string &strCommandName, RemoteCommandParser pfnParser,
64                                 size_t minArgumentCount, const std::string &strHelp,
65                                 const std::string &strDescription)
66            : _strCommandName(strCommandName), _pfnParser(pfnParser),
67              _minArgumentCount(minArgumentCount), _strHelp(strHelp),
68              _strDescription(strDescription)
69        {
70        }
71
72        const std::string &getCommandName() const { return _strCommandName; }
73
74        const std::string &getDescription() const { return _strDescription; }
75
76        // Usage
77        std::string usage() const { return _strCommandName + " " + _strHelp; }
78
79        bool parse(CCommandParser *pCommandParser, const IRemoteCommand &remoteCommand,
80                   std::string &strResult) const
81        {
82            // Check enough arguments supplied
83            if (remoteCommand.getArgumentCount() < _minArgumentCount) {
84
85                strResult = std::string("Not enough arguments supplied\nUsage:\n") + usage();
86
87                return false;
88            }
89
90            switch ((pCommandParser->*_pfnParser)(remoteCommand, strResult)) {
91            case EDone:
92                strResult = "Done";
93            // Fall through intentionally
94            case ESucceeded:
95                return true;
96            case EShowUsage:
97                strResult = usage();
98            // Fall through intentionally
99            case EFailed:
100                return false;
101            }
102
103            return false;
104        }
105
106    private:
107        std::string _strCommandName;
108        RemoteCommandParser _pfnParser;
109        size_t _minArgumentCount;
110        std::string _strHelp;
111        std::string _strDescription;
112    };
113
114public:
115    TRemoteCommandHandlerTemplate(CCommandParser *pCommandParser)
116        : _pCommandParser(pCommandParser), _maxCommandUsageLength(0)
117    {
118        // Help Command
119        addCommandParser("help", NULL, 0, "", "Show commands description and usage");
120    }
121    ~TRemoteCommandHandlerTemplate()
122    {
123        // FIXME use unique_ptr
124        for (auto *parser : _remoteCommandParserVector) {
125
126            delete parser;
127        }
128    }
129
130    // Parsers
131    bool addCommandParser(const std::string &strCommandName, RemoteCommandParser pfnParser,
132                          size_t minArgumentCount, const std::string &strHelp,
133                          const std::string &strDescription)
134    {
135        if (findCommandParserItem(strCommandName)) {
136
137            // Already exists
138            return false;
139        }
140
141        // Add command
142        _remoteCommandParserVector.push_back(new CRemoteCommandParserItem(
143            strCommandName, pfnParser, minArgumentCount, strHelp, strDescription));
144
145        return true;
146    }
147
148private:
149    // Command processing
150    bool remoteCommandProcess(const IRemoteCommand &remoteCommand, std::string &strResult)
151    {
152        // Dispatch
153        const CRemoteCommandParserItem *pRemoteCommandParserItem =
154            findCommandParserItem(remoteCommand.getCommand());
155
156        if (!pRemoteCommandParserItem) {
157
158            // Not found
159            strResult = "Command not found!\nUse \"help\" to show available commands";
160
161            return false;
162        }
163
164        if (remoteCommand.getCommand() == "help") {
165
166            helpCommandProcess(strResult);
167
168            return true;
169        }
170
171        return pRemoteCommandParserItem->parse(_pCommandParser, remoteCommand, strResult);
172    }
173
174    // Max command usage length, use for formatting
175    void initMaxCommandUsageLength()
176    {
177        if (!_maxCommandUsageLength) {
178            // Show usages
179            for (const auto *pRemoteCommandParserItem : _remoteCommandParserVector) {
180
181                size_t remoteCommandUsageLength = pRemoteCommandParserItem->usage().length();
182
183                if (remoteCommandUsageLength > _maxCommandUsageLength) {
184
185                    _maxCommandUsageLength = remoteCommandUsageLength;
186                }
187            }
188        }
189    }
190
191    /////////////////// Remote command parsers
192    /// Help
193    void helpCommandProcess(std::string &strResult)
194    {
195        initMaxCommandUsageLength();
196
197        // Show usages
198        for (const auto *pRemoteCommandParserItem : _remoteCommandParserVector) {
199
200            std::string strUsage = pRemoteCommandParserItem->usage();
201
202            // Align
203            size_t spacesToAdd = _maxCommandUsageLength + 5 - strUsage.length();
204
205            strResult += strUsage + std::string(spacesToAdd, ' ') + "=> " +
206                         pRemoteCommandParserItem->getDescription() + '\n';
207        }
208    }
209
210    const CRemoteCommandParserItem *findCommandParserItem(const std::string &strCommandName) const
211    {
212        for (const auto *pRemoteCommandParserItem : _remoteCommandParserVector) {
213
214            if (pRemoteCommandParserItem->getCommandName() == strCommandName) {
215
216                return pRemoteCommandParserItem;
217            }
218        }
219        return NULL;
220    }
221
222private:
223    CCommandParser *_pCommandParser;
224    std::vector<CRemoteCommandParserItem *> _remoteCommandParserVector;
225    size_t _maxCommandUsageLength;
226};
227