File indexing completed on 2024-09-22 04:57:52

0001 /*
0002     SPDX-FileCopyrightText: 2018 Ivan Čukić <ivan.cukic(at)kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 
0007 #ifndef VOY_TRASNPORT_ASIO_PROCESS_H
0008 #define VOY_TRASNPORT_ASIO_PROCESS_H
0009 
0010 // STL
0011 #include <string>
0012 #include <iostream>
0013 
0014 // Boost
0015 #include <boost/process.hpp>
0016 #include <boost/algorithm/cxx11/copy_if.hpp>
0017 #include <boost/asio/read_until.hpp>
0018 
0019 // Self
0020 #include "../../utils.h"
0021 #include "service.h"
0022 
0023 namespace voy::engine::asio {
0024 
0025 using utils::non_copyable;
0026 
0027 template <typename Handler>
0028 class process: non_copyable {
0029 public:
0030     template <typename Tuple, size_t ...Idx>
0031     auto make_process_impl(Tuple&& args, std::index_sequence<Idx...>)
0032     {
0033         // utils::print_types<Tuple>();
0034         return boost::process::child(std::get<Idx>(std::move(args))..., boost::process::std_out > m_pipe);
0035     }
0036 
0037     template <typename ...Args>
0038     decltype(auto) make_process(std::tuple<Args...> args)
0039     {
0040         return make_process_impl(std::move(args),
0041                                  std::index_sequence_for<Args...>());
0042     }
0043 
0044 
0045 
0046     template <typename ...Args>
0047     process(Handler handler, std::tuple<Args...> args)
0048         : m_handler{std::move(handler)}
0049         , m_pipe(service::instance())
0050         , m_process{make_process(args)}
0051         // , m_process(args, boost::process::std_out > m_pipe)
0052     {
0053         read_next();
0054     }
0055 
0056     ~process()
0057     {
0058     }
0059 
0060     process(process&& other) = delete;
0061     process& operator=(process&& other) = delete;
0062 
0063     template <typename T>
0064     void notify(T&& value)
0065     {
0066         voy_fwd_invoke(m_handler, value);
0067     }
0068 
0069     void init_handler()
0070     {
0071         m_handler.init();
0072     }
0073 
0074     void read_next()
0075     {
0076         boost::asio::async_read_until(
0077                 m_pipe, m_buffer, "\n",
0078                 [&] (boost::system::error_code ec, size_t count) {
0079                     // std::cerr << "One read finished: " << ec.message() << " count:" << count << std::endl;
0080 
0081                     if (ec && !(ec.value() == boost::asio::error::eof)) {
0082                         std::cerr << "Unknown error: " << ec.message() << std::endl;
0083                         m_handler.notify_ended();
0084                         return;
0085                     }
0086 
0087                     // std::string line;
0088                     std::istream stream(&m_buffer);
0089                     auto line_begin = std::istreambuf_iterator<char>(stream);
0090                     const auto buffer_end = std::istreambuf_iterator<char>();
0091 
0092                     // std::string s(line_begin, buffer_end);
0093                     // std::cerr << "READ [" << s << "]\n";
0094 
0095                     for (;;) {
0096                         auto is_newline = [] (char c) { return c == '\n'; };
0097 
0098                         std::string line;
0099                         line.reserve(256);
0100 
0101                         auto [ next, output ] = boost::algorithm::copy_until(
0102                                 line_begin, buffer_end,
0103                                 std::back_inserter(line),
0104                                 is_newline);
0105 
0106                         line_begin = next;
0107 
0108                         if (buffer_end == line_begin) {
0109                             m_previous_line = line;
0110                             break;
0111                         }
0112 
0113                         auto to_send = m_previous_line + line;
0114                         std::invoke(m_handler, to_send);
0115                         m_previous_line.clear();
0116 
0117                         ++line_begin;
0118 
0119                         if (buffer_end == line_begin) {
0120                             break;
0121                         }
0122                     }
0123 
0124                     if (!ec) {
0125                         read_next();
0126 
0127                     } else {
0128                         std::invoke(m_handler, std::move(m_previous_line));
0129                         m_handler.notify_ended();
0130                     }
0131                 });
0132     }
0133 
0134 private:
0135     Handler m_handler;
0136     boost::process::async_pipe m_pipe;
0137     boost::process::child m_process;
0138     boost::asio::streambuf m_buffer;
0139     std::string m_previous_line;
0140 };
0141 
0142 } // namespace voy::engine::asio
0143 
0144 #endif // include guard
0145