LCOV - code coverage report
Current view: top level - wirestead/builder - ibuilder.hpp (source / functions) Coverage Total Hit
Test: Wirestead Coverage Report Lines: 98.2 % 57 56
Test Date: 2026-08-30 10:35:09 Functions: 96.6 % 497 480
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 75.0 % 4 3

             Branch data     Line data    Source code
       1                 :             : /*
       2                 :             :  * Copyright 2025 Jinwoo Sung
       3                 :             :  *
       4                 :             :  * Licensed under the Apache License, Version 2.0 (the "License");
       5                 :             :  * you may not use this file except in compliance with the License.
       6                 :             :  * You may obtain a copy of the License at
       7                 :             :  *
       8                 :             :  *     http://www.apache.org/licenses/LICENSE-2.0
       9                 :             :  *
      10                 :             :  * Unless required by applicable law or agreed to in writing, software
      11                 :             :  * distributed under the License is distributed on an "AS IS" BASIS,
      12                 :             :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      13                 :             :  * See the License for the specific language governing permissions and
      14                 :             :  * limitations under the License.
      15                 :             :  */
      16                 :             : 
      17                 :             : #pragma once
      18                 :             : 
      19                 :             : #include <concepts>
      20                 :             : #include <functional>
      21                 :             : #include <memory>
      22                 :             : #include <string>
      23                 :             : #include <string_view>
      24                 :             : #include <utility>
      25                 :             : #include <vector>
      26                 :             : 
      27                 :             : #include "wirestead/base/constants.hpp"
      28                 :             : #include "wirestead/base/visibility.hpp"
      29                 :             : #include "wirestead/framer/iframer.hpp"
      30                 :             : #include "wirestead/framer/length_prefix_framer.hpp"
      31                 :             : #include "wirestead/framer/line_framer.hpp"
      32                 :             : #include "wirestead/framer/packet_framer.hpp"
      33                 :             : #include "wirestead/memory/safe_span.hpp"
      34                 :             : #include "wirestead/wrapper/context.hpp"
      35                 :             : 
      36                 :             : namespace wirestead {
      37                 :             : namespace builder {
      38                 :             : 
      39                 :             : /**
      40                 :             :  * @brief Concepts for callback signature validation
      41                 :             :  */
      42                 :             : template <typename T>
      43                 :             : concept DataHandler = std::invocable<T, const wrapper::MessageContext&>;
      44                 :             : 
      45                 :             : template <typename T>
      46                 :             : concept BatchDataHandler = std::invocable<T, const std::vector<wrapper::MessageContext>&>;
      47                 :             : 
      48                 :             : template <typename T>
      49                 :             : concept ErrorHandler = std::invocable<T, const wrapper::ErrorContext&>;
      50                 :             : 
      51                 :             : template <typename T>
      52                 :             : concept ConnectionHandler = std::invocable<T, const wrapper::ConnectionContext&>;
      53                 :             : 
      54                 :             : /**
      55                 :             :  * @brief Generic Builder interface for fluent API pattern
      56                 :             :  */
      57                 :             : template <typename T, typename Derived>
      58                 :             : class BuilderInterface {
      59                 :             :  public:
      60                 :         252 :   virtual ~BuilderInterface() = default;
      61                 :         252 :   BuilderInterface() = default;
      62                 :             :   BuilderInterface(const BuilderInterface&) = default;
      63                 :             :   BuilderInterface(BuilderInterface&&) = default;
      64                 :             :   BuilderInterface& operator=(const BuilderInterface&) = default;
      65                 :             :   BuilderInterface& operator=(BuilderInterface&&) = default;
      66                 :             : 
      67                 :             :   /**
      68                 :             :    * @brief Build and return the configured product
      69                 :             :    */
      70                 :             :   virtual std::unique_ptr<T> build() = 0;
      71                 :             : 
      72                 :             :   /**
      73                 :             :    * @brief Enable auto-manage functionality
      74                 :             :    */
      75                 :             :   virtual Derived& auto_start(bool auto_start = true) = 0;
      76                 :             : 
      77                 :             :   /**
      78                 :             :    * @brief Set data handler callback
      79                 :             :    */
      80                 :             :   template <DataHandler F>
      81                 :         211 :   Derived& on_data(F&& handler) {
      82                 :         211 :     on_data_ = std::forward<F>(handler);
      83                 :         211 :     return static_cast<Derived&>(*this);
      84                 :             :   }
      85                 :             : 
      86                 :             :   /**
      87                 :             :    * @brief Set batched data handler callback
      88                 :             :    */
      89                 :             :   template <BatchDataHandler F>
      90                 :           7 :   Derived& on_data_batch(F&& handler) {
      91                 :           7 :     on_data_batch_ = std::forward<F>(handler);
      92                 :           7 :     return static_cast<Derived&>(*this);
      93                 :             :   }
      94                 :             : 
      95                 :             :   /**
      96                 :             :    * @brief Set data handler callback using member function pointer
      97                 :             :    */
      98                 :             :   template <typename U, typename F>
      99                 :             :   Derived& on_data(U* obj, F method) {
     100                 :             :     return on_data([obj, method](const wrapper::MessageContext& ctx) { (obj->*method)(ctx); });
     101                 :             :   }
     102                 :             : 
     103                 :             :   /**
     104                 :             :    * @brief Set connection handler callback
     105                 :             :    */
     106                 :             :   template <ConnectionHandler F>
     107                 :          39 :   Derived& on_connect(F&& handler) {
     108                 :          39 :     on_connect_ = std::forward<F>(handler);
     109                 :          39 :     return static_cast<Derived&>(*this);
     110                 :             :   }
     111                 :             : 
     112                 :             :   /**
     113                 :             :    * @brief Set connection handler callback using member function pointer
     114                 :             :    */
     115                 :             :   template <typename U, typename F>
     116                 :             :   Derived& on_connect(U* obj, F method) {
     117                 :             :     return on_connect([obj, method](const wrapper::ConnectionContext& ctx) { (obj->*method)(ctx); });
     118                 :             :   }
     119                 :             : 
     120                 :             :   /**
     121                 :             :    * @brief Set disconnection handler callback
     122                 :             :    */
     123                 :             :   template <ConnectionHandler F>
     124                 :          18 :   Derived& on_disconnect(F&& handler) {
     125                 :          18 :     on_disconnect_ = std::forward<F>(handler);
     126                 :          18 :     return static_cast<Derived&>(*this);
     127                 :             :   }
     128                 :             : 
     129                 :             :   /**
     130                 :             :    * @brief Set disconnection handler callback using member function pointer
     131                 :             :    */
     132                 :             :   template <typename U, typename F>
     133                 :             :   Derived& on_disconnect(U* obj, F method) {
     134                 :             :     return on_disconnect([obj, method](const wrapper::ConnectionContext& ctx) { (obj->*method)(ctx); });
     135                 :             :   }
     136                 :             : 
     137                 :             :   /**
     138                 :             :    * @brief Set error handler callback
     139                 :             :    */
     140                 :             :   template <ErrorHandler F>
     141                 :         215 :   Derived& on_error(F&& handler) {
     142                 :         215 :     on_error_ = std::forward<F>(handler);
     143                 :         215 :     return static_cast<Derived&>(*this);
     144                 :             :   }
     145                 :             : 
     146                 :             :   /**
     147                 :             :    * @brief Set error handler callback using member function pointer
     148                 :             :    */
     149                 :             :   template <typename U, typename F>
     150                 :             :   Derived& on_error(U* obj, F method) {
     151                 :             :     return on_error([obj, method](const wrapper::ErrorContext& ctx) { (obj->*method)(ctx); });
     152                 :             :   }
     153                 :             : 
     154                 :             :   // Framing Support
     155                 :             : 
     156                 :             :   /**
     157                 :             :    * @brief Set a custom framer factory.
     158                 :             :    *
     159                 :             :    * The escape hatch for any protocol the two built-in framers do not fit -
     160                 :             :    * most often a length-prefixed binary layout, where PacketFramer's
     161                 :             :    * delimiter search is unsafe. See PacketFramer's warning.
     162                 :             :    */
     163                 :           4 :   Derived& framer(std::function<std::unique_ptr<framer::IFramer>()> factory) {
     164                 :           4 :     framer_factory_ = std::move(factory);
     165                 :           4 :     return static_cast<Derived&>(*this);
     166                 :             :   }
     167                 :             : 
     168                 :             :   /**
     169                 :             :    * @brief Activate line-delimited framing
     170                 :             :    */
     171                 :           7 :   Derived& use_line_framer(std::string_view delimiter = "\n", bool include_delimiter = false,
     172                 :             :                            size_t max_length = 65536) {
     173                 :           7 :     std::string delim(delimiter);
     174                 :          10 :     framer_factory_ = [delim, include_delimiter, max_length]() {
     175                 :          23 :       return std::make_unique<framer::LineFramer>(delim, include_delimiter, max_length);
     176                 :             :     };
     177                 :           7 :     return static_cast<Derived&>(*this);
     178                 :           7 :   }
     179                 :             : 
     180                 :             :   /**
     181                 :             :    * @brief Activate length-prefixed binary framing.
     182                 :             :    *
     183                 :             :    * The layout most binary protocols use, and the one use_packet_framer()
     184                 :             :    * cannot carry: the length says how many bytes to collect, so no byte value
     185                 :             :    * is special and the payload may contain anything.
     186                 :             :    *
     187                 :             :    * Requires the stream to start on a frame boundary - the normal case for TCP
     188                 :             :    * and UDS. See LengthPrefixFramer for what that rules out.
     189                 :             :    */
     190                 :           1 :   Derived& use_length_prefix_framer(size_t prefix_bytes = 2,
     191                 :             :                                     framer::LengthPrefixFramer::Endian endian = framer::LengthPrefixFramer::Endian::Big,
     192                 :             :                                     size_t max_length = 65536, bool length_includes_prefix = false) {
     193                 :           1 :     framer_factory_ = [prefix_bytes, endian, max_length, length_includes_prefix]() {
     194                 :           1 :       return std::make_unique<framer::LengthPrefixFramer>(prefix_bytes, endian, max_length, length_includes_prefix);
     195                 :             :     };
     196                 :           1 :     return static_cast<Derived&>(*this);
     197                 :             :   }
     198                 :             : 
     199                 :             :   /**
     200                 :             :    * @brief Activate binary packet framing between a start and end pattern.
     201                 :             :    *
     202                 :             :    * @warning Only safe when the payload cannot contain the end pattern - there
     203                 :             :    * is no escaping, so the first occurrence ends the frame wherever it falls
     204                 :             :    * and a binary payload gets silently truncated. Use
     205                 :             :    * use_length_prefix_framer() for length-prefixed protocols. See PacketFramer
     206                 :             :    * for the detail.
     207                 :             :    */
     208                 :           5 :   Derived& use_packet_framer(const std::vector<uint8_t>& start_pattern, const std::vector<uint8_t>& end_pattern,
     209                 :             :                              size_t max_length) {
     210                 :           7 :     framer_factory_ = [start_pattern, end_pattern, max_length]() {
     211                 :           3 :       return std::make_unique<framer::PacketFramer>(start_pattern, end_pattern, max_length);
     212                 :             :     };
     213                 :           5 :     return static_cast<Derived&>(*this);
     214                 :             :   }
     215                 :             : 
     216                 :             :   /**
     217                 :             :    * @brief Set message handler callback
     218                 :             :    */
     219                 :             :   template <DataHandler F>
     220                 :          13 :   Derived& on_message(F&& handler) {
     221                 :          13 :     on_message_ = std::forward<F>(handler);
     222                 :          13 :     return static_cast<Derived&>(*this);
     223                 :             :   }
     224                 :             : 
     225                 :             :   /**
     226                 :             :    * @brief Set batched framed-message handler callback
     227                 :             :    */
     228                 :             :   template <BatchDataHandler F>
     229                 :           7 :   Derived& on_message_batch(F&& handler) {
     230                 :           7 :     on_message_batch_ = std::forward<F>(handler);
     231                 :           7 :     return static_cast<Derived&>(*this);
     232                 :             :   }
     233                 :             : 
     234                 :             :   /**
     235                 :             :    * @brief Set backpressure notification callback
     236                 :             :    */
     237                 :          11 :   Derived& on_backpressure(std::function<void(size_t)> handler) {
     238                 :          11 :     on_backpressure_ = std::move(handler);
     239                 :          11 :     return static_cast<Derived&>(*this);
     240                 :             :   }
     241                 :             : 
     242                 :             :   /**
     243                 :             :    * @brief Set message handler callback using member function pointer
     244                 :             :    */
     245                 :             :   template <typename U, typename F>
     246                 :             :   Derived& on_message(U* obj, F method) {
     247                 :             :     return on_message([obj, method](const wrapper::MessageContext& ctx) { (obj->*method)(ctx); });
     248                 :             :   }
     249                 :             : 
     250                 :             :   /**
     251                 :             :    * @brief Set the backpressure strategy
     252                 :             :    */
     253                 :          14 :   Derived& backpressure_strategy(base::constants::BackpressureStrategy strategy) {
     254                 :          14 :     bp_strategy_ = strategy;
     255                 :          14 :     bp_strategy_set_ = true;
     256                 :          14 :     return static_cast<Derived&>(*this);
     257                 :             :   }
     258                 :             : 
     259                 :             :   /**
     260                 :             :    * @brief Set the backpressure threshold in bytes
     261                 :             :    */
     262                 :          14 :   Derived& backpressure_threshold(size_t threshold) {
     263                 :          14 :     bp_threshold_ = threshold;
     264                 :          14 :     bp_threshold_set_ = true;
     265                 :          14 :     return static_cast<Derived&>(*this);
     266                 :             :   }
     267                 :             : 
     268                 :             :  protected:
     269                 :         239 :   size_t get_effective_backpressure_threshold() const {
     270         [ +  + ]:         239 :     if (bp_threshold_set_) {
     271                 :          14 :       return bp_threshold_;
     272                 :             :     }
     273         [ -  + ]:         225 :     if (bp_strategy_ == base::constants::BackpressureStrategy::BestEffort) {
     274                 :           0 :       return base::constants::DEFAULT_THRESHOLD_BEST_EFFORT;
     275                 :             :     }
     276                 :         225 :     return base::constants::DEFAULT_THRESHOLD_RELIABLE;
     277                 :             :   }
     278                 :             : 
     279                 :             :   std::function<std::unique_ptr<framer::IFramer>()> framer_factory_;
     280                 :             :   std::function<void(const wrapper::MessageContext&)> on_data_;
     281                 :             :   std::function<void(const wrapper::ConnectionContext&)> on_connect_;
     282                 :             :   std::function<void(const wrapper::ConnectionContext&)> on_disconnect_;
     283                 :             :   std::function<void(const wrapper::ErrorContext&)> on_error_;
     284                 :             :   std::function<void(const wrapper::MessageContext&)> on_message_;
     285                 :             :   std::function<void(const std::vector<wrapper::MessageContext>&)> on_data_batch_;
     286                 :             :   std::function<void(const std::vector<wrapper::MessageContext>&)> on_message_batch_;
     287                 :             :   std::function<void(size_t)> on_backpressure_;
     288                 :             : 
     289                 :             :   base::constants::BackpressureStrategy bp_strategy_{base::constants::BackpressureStrategy::Reliable};
     290                 :             :   size_t bp_threshold_{0};
     291                 :             :   bool bp_strategy_set_{false};
     292                 :             :   bool bp_threshold_set_{false};
     293                 :             : };
     294                 :             : 
     295                 :             : }  // namespace builder
     296                 :             : }  // namespace wirestead
        

Generated by: LCOV version 2.0-1