Webthing-CPP: a modern CPP implementation of the WebThings API


LCOV - code coverage report
Current view: top level - webthing - action.hpp (source / functions) Coverage Total Hit
Test: filtered_coverage.info Lines: 96.8 % 95 92
Test Date: 2025-03-15 12:45:00 Functions: 82.8 % 58 48

            Line data    Source code
       1              : // Webthing-CPP
       2              : // SPDX-FileCopyrightText: 2023-present Benno Waldhauer
       3              : // SPDX-License-Identifier: MIT
       4              : 
       5              : #pragma once
       6              : 
       7              : #include <optional>
       8              : #include <bw/webthing/json.hpp>
       9              : #include <bw/webthing/utils.hpp>
      10              : 
      11              : namespace bw::webthing {
      12              : 
      13              : class Action;
      14              : 
      15              : json action_status_message(const Action& action);
      16              : json action_status_message(std::shared_ptr<Action> action);
      17              : 
      18              : struct ActionBehavior
      19              : {
      20              :     std::function<void (json)> notify_thing;
      21              :     std::function<void ()> perform_action;
      22              :     std::function<void ()> cancel_action;
      23              :     std::function<void* ()> get_thing;
      24              : };
      25              : 
      26              : 
      27            5 : template<class T> ActionBehavior make_action_behavior(T* thing,
      28              :     std::function<void ()> perform_action = nullptr,
      29              :     std::function<void ()> cancel_action = nullptr)
      30              : {
      31              :     return { 
      32            6 :         [thing](auto action_status){ thing->action_notify(action_status); },
      33            5 :         std::move(perform_action),
      34            5 :         std::move(cancel_action),
      35            0 :         [thing]{ return thing; }
      36           10 :     };
      37            5 : }
      38              : 
      39              : template <typename T, typename = void>
      40              : struct has_perform_action : std::false_type {};
      41              : 
      42              : template <typename T>
      43              : struct has_perform_action<T, std::void_t<decltype(&T::perform_action)>> : std::true_type {};
      44              : 
      45              : template <typename T, typename = void>
      46              : struct has_cancel_action : std::false_type {};
      47              : 
      48              : template <typename T>
      49              : struct has_cancel_action<T, std::void_t<decltype(&T::cancel_action)>> : std::true_type {};
      50              : 
      51              : template <typename T>
      52              : typename std::enable_if<has_cancel_action<T>::value>::type
      53            1 : execute_cancel_action(T& action_impl)
      54              : {
      55            1 :     (action_impl.*&T::cancel_action)();
      56            1 : }
      57              : 
      58              : template <typename T>
      59              : typename std::enable_if<!has_cancel_action<T>::value>::type
      60            1 : execute_cancel_action(T& action_impl)
      61              : {
      62              :     // action_impl has no cancel_action that could be executed
      63            1 : }
      64              : 
      65            5 : template<class T, class A> ActionBehavior make_action_behavior(T* thing, A* action_impl)
      66              : {
      67              :     return {
      68           20 :         [thing](auto action_status){ thing->action_notify(action_status); },
      69           15 :         [action_impl]{ action_impl->perform_action(); },
      70           12 :         [action_impl]{ execute_cancel_action(*action_impl); },
      71            7 :         [thing]{ return thing; }
      72           10 :     };
      73            5 : }
      74              : 
      75              : //An Action represents an individual action on a thing.
      76              : class Action : public std::enable_shared_from_this<Action> 
      77              : {
      78              : public:
      79           21 :     Action(std::string id, ActionBehavior action_behavior, std::string name, std::optional<json> input = std::nullopt)
      80           21 :         : id(id)
      81           21 :         , action_behavior(action_behavior)
      82           21 :         , name(name)
      83           21 :         , input(input)
      84           21 :         , href("/actions/" + name + "/" + id)
      85           42 :         , status("created")
      86           63 :         , time_requested(bw::webthing::timestamp())
      87              :     {
      88           21 :     }
      89              : 
      90            5 :     template<class T, class A> Action(std::string id, T* thing, A* action_impl, std::string name, std::optional<json> input = std::nullopt)
      91            5 :         : Action(id, make_action_behavior(thing, action_impl), name, input)
      92              :     {
      93            5 :     }
      94              : 
      95              :     // Get the action description of the action as a json object.
      96           49 :     json as_action_description() const
      97              :     {
      98           49 :         json description;
      99           49 :         description[name]["href"] = href_prefix + href;
     100           49 :         description[name]["timeRequested"] = time_requested;
     101           49 :         description[name]["status"] = status;
     102              : 
     103           49 :         if(input)
     104           41 :             description[name]["input"] = *input;
     105              : 
     106           49 :         if(time_completed)
     107           16 :             description[name]["timeCompleted"] = *time_completed;
     108              : 
     109           49 :         return description;
     110            0 :     }
     111              : 
     112              :     // Set the prefix of any hrefs associated with this action.
     113           19 :     void set_href_prefix(const std::string& prefix)
     114              :     {
     115           19 :         href_prefix = prefix;
     116           19 :     }
     117              : 
     118           16 :     std::string get_id() const
     119              :     {
     120           16 :         return id;
     121              :     }
     122              : 
     123            4 :     std::string get_name() const
     124              :     {
     125            4 :         return name;
     126              :     }
     127              : 
     128            2 :     std::string get_href() const
     129              :     {
     130            2 :         return href_prefix + href;
     131              :     }
     132              : 
     133            8 :     std::string get_status() const
     134              :     {  
     135            8 :         return status;
     136              :     }
     137              : 
     138            2 :     std::string get_time_requested() const
     139              :     {
     140            2 :         return time_requested;
     141              :     }   
     142              : 
     143            4 :     std::optional<std::string> get_time_completed() const
     144              :     {
     145            4 :         return time_completed;
     146              :     }
     147              : 
     148           11 :     std::optional<json> get_input() const
     149              :     {
     150           11 :         return input;
     151              :     }
     152              : 
     153            4 :     template<class T> T* get_thing()
     154              :     {
     155            4 :         if(action_behavior.get_thing)
     156            4 :             return static_cast<T*>(action_behavior.get_thing());
     157            0 :         return nullptr;
     158              :     }
     159              : 
     160              :     // Start performing the action.
     161           10 :     void start()
     162              :     {
     163           10 :         status = "pending";
     164           10 :         notify_thing();
     165           10 :         perform_action();
     166           10 :         finish();
     167           10 :     }
     168              : 
     169              :     // Finish performing the action.
     170           10 :     void finish()
     171              :     {
     172           10 :         status = "completed";
     173           10 :         time_completed = timestamp();
     174           10 :         notify_thing();
     175           10 :     }
     176              : 
     177           10 :     void perform_action()
     178              :     {
     179           10 :         if(action_behavior.perform_action)
     180           10 :             action_behavior.perform_action();
     181           10 :     }
     182              : 
     183            5 :     void cancel()
     184              :     {
     185            5 :         if(action_behavior.cancel_action)
     186            3 :             action_behavior.cancel_action();
     187            5 :     }
     188              : 
     189              : private:
     190           20 :     void notify_thing()
     191              :     {
     192           20 :         if(action_behavior.notify_thing)
     193              :         {
     194              :             try
     195              :             {
     196              :                 // Pass this as shared_ptr to ensure Action remains alive during callback execution
     197              :                 // this might be necessary, as an Action is interacted with from different threads.
     198           20 :                 action_behavior.notify_thing(action_status_message(shared_from_this()));
     199              :             }
     200            4 :             catch(std::bad_weak_ptr&)
     201              :             {
     202            4 :                 action_behavior.notify_thing(action_status_message(*this));
     203            4 :             }
     204              :         }
     205           20 :     }
     206              : 
     207              :     std::string id;
     208              :     ActionBehavior action_behavior;
     209              :     std::string name;
     210              :     std::optional<json> input;
     211              :     std::string href_prefix;
     212              :     std::string href;
     213              :     std::string status;
     214              :     std::string time_requested;
     215              :     std::optional<std::string> time_completed;
     216              : };
     217              : 
     218           38 : inline json action_status_message(const Action& action)
     219              : {
     220           38 :     json description = action.as_action_description();
     221              :     return json({
     222              :         {"messageType", "actionStatus"}, 
     223              :         {"data", description}
     224          304 :     });
     225          228 : }
     226              : 
     227           33 : inline json action_status_message(std::shared_ptr<Action> action)
     228              : {
     229           33 :     return action_status_message(*action);
     230              : }
     231              : 
     232              : } // bw::webthing
        

Generated by: LCOV version 2.0-1