LCOV - code coverage report
Current view: top level - ezlibs - ezCmdProc.hpp (source / functions) Coverage Total Hit
Test: Coverage (llvm-cov → lcov → genhtml) Lines: 98.8 % 80 79
Test Date: 2025-09-16 22:55:37 Functions: 100.0 % 5 5
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 79.4 % 34 27

             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
        

Generated by: LCOV version 2.0-1