From 00dace075cf3c4ff743d9f704724fed224d04fa5 Mon Sep 17 00:00:00 2001 From: seele Date: Tue, 24 Mar 2026 16:35:31 +0800 Subject: [PATCH 1/5] feat: enhance ServiceImpl to accept CatterRuntime and update runtime handling --- src/catter/main.cc | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/catter/main.cc b/src/catter/main.cc index 53716a1..5373c4e 100644 --- a/src/catter/main.cc +++ b/src/catter/main.cc @@ -30,7 +30,7 @@ using namespace catter; class ServiceImpl : public ipc::InjectService { public: - ServiceImpl(data::ipcid_t id) : id(id) {}; + ServiceImpl(data::ipcid_t id, const js::CatterRuntime* runtime) : id(id), runtime(runtime) {}; ~ServiceImpl() override = default; data::ipcid_t create(data::ipcid_t parent_id) override { @@ -45,18 +45,20 @@ class ServiceImpl : public ipc::InjectService { .exe = cmd.executable, .argv = cmd.args, .env = cmd.env, - // .runtime = + .runtime = *runtime, .parent = this->parent_id, }); switch(act.type()) { - case js::ActionType::drop: + case js::ActionType::drop: { + return data::action{.type = data::action::DROP, .cmd = {}}; + } case js::ActionType::skip: { return data::action{.type = data::action::INJECT, .cmd = cmd}; } case js::ActionType::modify: { - auto tag = act.get(); + auto& tag = act.get(); return data::action{ .type = data::action::INJECT, @@ -81,14 +83,17 @@ class ServiceImpl : public ipc::InjectService { } struct Factory { + const js::CatterRuntime* runtime; + std::unique_ptr operator() (data::ipcid_t id) { - return std::make_unique(id); + return std::make_unique(id, runtime); } }; private: data::ipcid_t id = 0; data::ipcid_t parent_id = 0; + const js::CatterRuntime* runtime = nullptr; }; struct Config { @@ -98,7 +103,7 @@ struct Config { std::vector script_args; std::vector build_system_command; std::filesystem::path working_dir; - js::CatterRuntime runtime; + const js::CatterRuntime* runtime; }; Config extract_config(const core::Option::CatterOption& opt) { @@ -131,7 +136,7 @@ Config extract_config(const core::Option::CatterOption& opt) { if(auto it = mode_map.find(*opt.mode); it != mode_map.end()) { config.mode = it->second.mode; - config.runtime = it->second.runtime; + config.runtime = &it->second.runtime; } else { throw std::runtime_error(std::format("Unsupported mode: {}", *opt.mode)); } @@ -161,7 +166,7 @@ void inject(const Config& config) { .scriptPath = config.script_path, .scriptArgs = config.script_args, .buildSystemCommand = config.build_system_command, - .runtime = config.runtime, + .runtime = *config.runtime, .options = { .log = config.log, @@ -171,7 +176,8 @@ void inject(const Config& config) { Session session; - auto ret = session.run(new_config.buildSystemCommand, ServiceImpl::Factory{}); + auto ret = + session.run(new_config.buildSystemCommand, ServiceImpl::Factory{.runtime = config.runtime}); js::on_finish(js::Tag{ .code = ret, From db97209814e631889ea4f88eb044761e8bec30f6 Mon Sep 17 00:00:00 2001 From: seele Date: Tue, 24 Mar 2026 17:05:32 +0800 Subject: [PATCH 2/5] feat: implement app configuration and runtime planning structures --- src/catter/core/app_config.cc | 74 +++++++++++++ src/catter/core/app_config.h | 38 +++++++ src/catter/core/app_runner.cc | 124 ++++++++++++++++++++++ src/catter/core/app_runner.h | 9 ++ src/catter/main.cc | 191 +--------------------------------- 5 files changed, 247 insertions(+), 189 deletions(-) create mode 100644 src/catter/core/app_config.cc create mode 100644 src/catter/core/app_config.h create mode 100644 src/catter/core/app_runner.cc create mode 100644 src/catter/core/app_runner.h diff --git a/src/catter/core/app_config.cc b/src/catter/core/app_config.cc new file mode 100644 index 0000000..2429d0e --- /dev/null +++ b/src/catter/core/app_config.cc @@ -0,0 +1,74 @@ +#include "app_config.h" + +#include +#include +#include +#include +#include + +namespace catter::app { + +StartupConfig to_startup_config(const core::Option::CatterOption& opt) { + return StartupConfig{ + .log = true, + .mode = *opt.mode, + .script_path = *opt.script_path, + .script_args = opt.script_args.has_value() ? *opt.script_args : std::vector{}, + .build_system_command = *opt.args, + .working_dir = opt.working_dir.has_value() ? std::filesystem::absolute(*opt.working_dir) + : std::filesystem::current_path(), + }; +} + +RuntimePlan build_runtime_plan(const StartupConfig& config) { + struct ModeMeta { + ipc::ServiceMode mode; + js::CatterRuntime runtime; + }; + + static const std::unordered_map mode_map = { + {"inject", + {.mode = ipc::ServiceMode::INJECT, + .runtime = { + .supportActions = {js::ActionType::drop, + js::ActionType::skip, + js::ActionType::modify}, + .supportEvents = {js::EventType::finish}, + .type = js::CatterRuntime::Type::inject, + .supportParentId = true, + }}} + }; + + auto it = mode_map.find(config.mode); + if(it == mode_map.end()) { + throw std::runtime_error(std::format("Unsupported mode: {}", config.mode)); + } + + return RuntimePlan{ + .log = config.log, + .mode = it->second.mode, + .script_path = config.script_path, + .script_args = config.script_args, + .build_system_command = config.build_system_command, + .working_dir = config.working_dir, + .runtime = &it->second.runtime, + }; +} + +std::string load_script_content(const std::string& script_path) { + if(script_path == "script::cdb") { + return R"( + import { scripts, service } from "catter"; + service.register(new scripts.CDB()); + )"; + } + + std::ifstream ifs{script_path}; + if(!ifs.good()) { + throw std::runtime_error(std::format("Failed to open script file: {}", script_path)); + } + + return std::string((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); +} + +} // namespace catter::app diff --git a/src/catter/core/app_config.h b/src/catter/core/app_config.h new file mode 100644 index 0000000..bfd4e0e --- /dev/null +++ b/src/catter/core/app_config.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include + +#include "capi/type.h" +#include "ipc.h" +#include "opt/main/option.h" + +namespace catter::app { + +struct StartupConfig { + bool log; + std::string mode; + std::string script_path; + std::vector script_args; + std::vector build_system_command; + std::filesystem::path working_dir; +}; + +struct RuntimePlan { + bool log; + ipc::ServiceMode mode; + std::string script_path; + std::vector script_args; + std::vector build_system_command; + std::filesystem::path working_dir; + const js::CatterRuntime* runtime; +}; + +StartupConfig to_startup_config(const core::Option::CatterOption& opt); + +RuntimePlan build_runtime_plan(const StartupConfig& config); + +std::string load_script_content(const std::string& script_path); + +} // namespace catter::app diff --git a/src/catter/core/app_runner.cc b/src/catter/core/app_runner.cc new file mode 100644 index 0000000..487668f --- /dev/null +++ b/src/catter/core/app_runner.cc @@ -0,0 +1,124 @@ +#include "app_runner.h" + +#include +#include +#include +#include +#include + +#include "app_config.h" +#include "ipc.h" +#include "js.h" +#include "qjs.h" +#include "session.h" +#include "util/data.h" + +namespace catter::app { +namespace { + +class ServiceImpl : public ipc::InjectService { +public: + ServiceImpl(data::ipcid_t id, const js::CatterRuntime* runtime) : id(id), runtime(runtime) {} + ~ServiceImpl() override = default; + + data::ipcid_t create(data::ipcid_t parent_id) override { + this->parent_id = parent_id; + return this->id; + } + + data::action make_decision(data::command cmd) override { + auto act = js::on_command(this->id, + js::CommandData{ + .cwd = cmd.cwd, + .exe = cmd.executable, + .argv = cmd.args, + .env = cmd.env, + .runtime = *runtime, + .parent = this->parent_id, + }); + + switch(act.type()) { + case js::ActionType::drop: { + return data::action{.type = data::action::DROP, .cmd = {}}; + } + case js::ActionType::skip: { + return data::action{.type = data::action::INJECT, .cmd = cmd}; + } + case js::ActionType::modify: { + auto& tag = act.get(); + return data::action{ + .type = data::action::INJECT, + .cmd = { + .cwd = std::move(tag.data.cwd), + .executable = std::move(tag.data.exe), + .args = std::move(tag.data.argv), + .env = std::move(tag.data.env), + }}; + } + default: throw std::runtime_error("Unhandled action type"); + } + } + + void finish(int64_t code) override { + js::on_execution(this->id, js::Tag{.code = code}); + } + + void report_error(data::ipcid_t parent_id, std::string error_msg) override { + js::on_command(id, std::unexpected(js::CatterErr{.msg = std::move(error_msg)})); + } + + struct Factory { + const js::CatterRuntime* runtime; + + std::unique_ptr operator() (data::ipcid_t id) { + return std::make_unique(id, runtime); + } + }; + +private: + data::ipcid_t id = 0; + data::ipcid_t parent_id = 0; + const js::CatterRuntime* runtime = nullptr; +}; + +void inject(const RuntimePlan& plan) { + auto script_content = load_script_content(plan.script_path); + js::run_js_file(script_content, plan.script_path); + + auto new_config = js::on_start({ + .scriptPath = plan.script_path, + .scriptArgs = plan.script_args, + .buildSystemCommand = plan.build_system_command, + .runtime = *plan.runtime, + .options = {.log = plan.log}, + .isScriptSupported = true, + }); + + Session session; + auto ret = session.run(new_config.buildSystemCommand, ServiceImpl::Factory{.runtime = plan.runtime}); + + js::on_finish(js::Tag{ + .code = ret, + }); +} + +} // namespace + +void run(const core::Option::CatterOption& opt) { + auto startup = to_startup_config(opt); + auto plan = build_runtime_plan(startup); + + js::init_qjs({.pwd = plan.working_dir}); + + switch(plan.mode) { + case ipc::ServiceMode::INJECT: { + inject(plan); + break; + } + default: { + throw std::runtime_error(std::format("UnExpected mode: {:0x}", static_cast(plan.mode))); + } + } +} + +} // namespace catter::app diff --git a/src/catter/core/app_runner.h b/src/catter/core/app_runner.h new file mode 100644 index 0000000..d79e00d --- /dev/null +++ b/src/catter/core/app_runner.h @@ -0,0 +1,9 @@ +#pragma once + +#include "opt/main/option.h" + +namespace catter::app { + +void run(const core::Option::CatterOption& opt); + +} // namespace catter::app diff --git a/src/catter/main.cc b/src/catter/main.cc index 5373c4e..51caebf 100644 --- a/src/catter/main.cc +++ b/src/catter/main.cc @@ -1,205 +1,18 @@ -#include -#include -#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include "capi/type.h" #include "opt/main/option.h" -#include "js.h" -#include "ipc.h" #include "qjs.h" -#include "session.h" +#include "app_runner.h" #include "util/crossplat.h" -#include "util/data.h" #include "util/log.h" #include "config/catter.h" using namespace catter; -class ServiceImpl : public ipc::InjectService { -public: - ServiceImpl(data::ipcid_t id, const js::CatterRuntime* runtime) : id(id), runtime(runtime) {}; - ~ServiceImpl() override = default; - - data::ipcid_t create(data::ipcid_t parent_id) override { - this->parent_id = parent_id; - return this->id; - } - - data::action make_decision(data::command cmd) override { - auto act = js::on_command(this->id, - js::CommandData{ - .cwd = cmd.cwd, - .exe = cmd.executable, - .argv = cmd.args, - .env = cmd.env, - .runtime = *runtime, - .parent = this->parent_id, - }); - - switch(act.type()) { - case js::ActionType::drop: { - return data::action{.type = data::action::DROP, .cmd = {}}; - } - case js::ActionType::skip: { - return data::action{.type = data::action::INJECT, .cmd = cmd}; - } - - case js::ActionType::modify: { - auto& tag = act.get(); - - return data::action{ - .type = data::action::INJECT, - .cmd = { - .cwd = std::move(tag.data.cwd), - .executable = std::move(tag.data.exe), - .args = std::move(tag.data.argv), - .env = std::move(tag.data.env), - } - }; - } - default: throw std::runtime_error("Unhandled action type"); - } - } - - void finish(int64_t code) override { - js::on_execution(this->id, js::Tag{.code = code}); - } - - void report_error(data::ipcid_t parent_id, std::string error_msg) override { - js::on_command(id, std::unexpected(js::CatterErr{.msg = std::move(error_msg)})); - } - - struct Factory { - const js::CatterRuntime* runtime; - - std::unique_ptr operator() (data::ipcid_t id) { - return std::make_unique(id, runtime); - } - }; - -private: - data::ipcid_t id = 0; - data::ipcid_t parent_id = 0; - const js::CatterRuntime* runtime = nullptr; -}; - -struct Config { - bool log; - ipc::ServiceMode mode; - std::string script_path; - std::vector script_args; - std::vector build_system_command; - std::filesystem::path working_dir; - const js::CatterRuntime* runtime; -}; - -Config extract_config(const core::Option::CatterOption& opt) { - struct mode_meta { - ipc::ServiceMode mode; - js::CatterRuntime runtime; - }; - - static std::unordered_map mode_map = { - {"inject", - {.mode = ipc::ServiceMode::INJECT, - .runtime = { - .supportActions = {js::ActionType::drop, - js::ActionType::skip, - js::ActionType::modify}, - .supportEvents = {js::EventType::finish}, - .type = js::CatterRuntime::Type::inject, - .supportParentId = true, - }}} - }; - - Config config{ - .log = true, - .script_path = *opt.script_path, - .script_args = opt.script_args.has_value() ? *opt.script_args : std::vector{}, - .build_system_command = *opt.args, - .working_dir = opt.working_dir.has_value() ? std::filesystem::absolute(*opt.working_dir) - : std::filesystem::current_path(), - }; - - if(auto it = mode_map.find(*opt.mode); it != mode_map.end()) { - config.mode = it->second.mode; - config.runtime = &it->second.runtime; - } else { - throw std::runtime_error(std::format("Unsupported mode: {}", *opt.mode)); - } - return config; -} - -void inject(const Config& config) { - if(config.script_path == "script::cdb") { - std::string_view script = R"( - import { scripts, service } from "catter"; - service.register(new scripts.CDB()); - )"; - js::run_js_file(script, config.script_path); - } else { - std::ifstream ifs{config.script_path}; - if(!ifs.good()) { - throw std::runtime_error( - std::format("Failed to open script file: {}", config.script_path)); - } - - std::string content((std::istreambuf_iterator(ifs)), - std::istreambuf_iterator()); - catter::js::run_js_file(content, config.script_path); - } - - auto new_config = js::on_start({ - .scriptPath = config.script_path, - .scriptArgs = config.script_args, - .buildSystemCommand = config.build_system_command, - .runtime = *config.runtime, - .options = - { - .log = config.log, - }, - .isScriptSupported = true - }); - - Session session; - - auto ret = - session.run(new_config.buildSystemCommand, ServiceImpl::Factory{.runtime = config.runtime}); - - js::on_finish(js::Tag{ - .code = ret, - }); -} - -void dispatch(const core::Option::CatterOption& opt) { - auto config = extract_config(opt); - - js::init_qjs({.pwd = config.working_dir}); - - switch(config.mode) { - case ipc::ServiceMode::INJECT: { - inject(config); - break; - } - default: { - throw std::runtime_error(std::format("UnExpected mode: {:0x}", (uint8_t)config.mode)); - } - } -} - int main(int argc, char* argv[]) { auto args = deco::util::argvify(argc, argv, 1); @@ -209,7 +22,7 @@ int main(int argc, char* argv[]) { cli.dispatch(core::Option::HelpOpt::category_info, [&](const core::Option& opt) { cli.usage(std::cout); }) .dispatch(core::Option::CatterOption::category_info, - [&](const auto& opt) { dispatch(opt.main_opt); }) + [&](const auto& opt) { app::run(opt.main_opt); }) .dispatch([&](const auto&) { cli.usage(std::cout); }) .when_err([&](const deco::cli::ParseError& err) { std::println("Error parsing options: {}", err.message); From 841b94752980d6cb3d41bcd998b61dbd6bbd4a28 Mon Sep 17 00:00:00 2001 From: seele Date: Tue, 24 Mar 2026 17:26:15 +0800 Subject: [PATCH 3/5] feat: enhance session management with ProcessLaunchPlan and update run method --- src/catter/core/app_config.cc | 13 +++++--- src/catter/core/app_runner.cc | 33 +++++++++++++++---- src/catter/core/session.h | 44 +++++++++++++++----------- tests/integration/test/catter-proxy.cc | 16 +++++++++- 4 files changed, 76 insertions(+), 30 deletions(-) diff --git a/src/catter/core/app_config.cc b/src/catter/core/app_config.cc index 2429d0e..20baa2b 100644 --- a/src/catter/core/app_config.cc +++ b/src/catter/core/app_config.cc @@ -26,7 +26,7 @@ RuntimePlan build_runtime_plan(const StartupConfig& config) { js::CatterRuntime runtime; }; - static const std::unordered_map mode_map = { + const static std::unordered_map mode_map = { {"inject", {.mode = ipc::ServiceMode::INJECT, .runtime = { @@ -56,11 +56,16 @@ RuntimePlan build_runtime_plan(const StartupConfig& config) { } std::string load_script_content(const std::string& script_path) { - if(script_path == "script::cdb") { - return R"( + const static std::unordered_map internal_scripts = { + {"script::cdb", + R"( import { scripts, service } from "catter"; service.register(new scripts.CDB()); - )"; + )"} + }; + + if(auto it = internal_scripts.find(script_path); it != internal_scripts.end()) { + return std::string(it->second); } std::ifstream ifs{script_path}; diff --git a/src/catter/core/app_runner.cc b/src/catter/core/app_runner.cc index 487668f..f50699a 100644 --- a/src/catter/core/app_runner.cc +++ b/src/catter/core/app_runner.cc @@ -11,6 +11,8 @@ #include "js.h" #include "qjs.h" #include "session.h" +#include "config/catter-proxy.h" +#include "util/crossplat.h" #include "util/data.h" namespace catter::app { @@ -19,6 +21,7 @@ namespace { class ServiceImpl : public ipc::InjectService { public: ServiceImpl(data::ipcid_t id, const js::CatterRuntime* runtime) : id(id), runtime(runtime) {} + ~ServiceImpl() override = default; data::ipcid_t create(data::ipcid_t parent_id) override { @@ -49,11 +52,12 @@ class ServiceImpl : public ipc::InjectService { return data::action{ .type = data::action::INJECT, .cmd = { - .cwd = std::move(tag.data.cwd), - .executable = std::move(tag.data.exe), - .args = std::move(tag.data.argv), - .env = std::move(tag.data.env), - }}; + .cwd = std::move(tag.data.cwd), + .executable = std::move(tag.data.exe), + .args = std::move(tag.data.argv), + .env = std::move(tag.data.env), + } + }; } default: throw std::runtime_error("Unhandled action type"); } @@ -94,8 +98,22 @@ void inject(const RuntimePlan& plan) { .isScriptSupported = true, }); + Session::ProcessLaunchPlan launch_plan; + launch_plan.executable = (util::get_catter_root_path() / config::proxy::EXE_NAME).string(); + launch_plan.args = { + launch_plan.executable, + "-p", + "0", + "--exec", + new_config.buildSystemCommand.front(), + "--", + }; + append_range_to_vector(launch_plan.args, new_config.buildSystemCommand); + Session session; - auto ret = session.run(new_config.buildSystemCommand, ServiceImpl::Factory{.runtime = plan.runtime}); + auto session_plan = Session::make_run_plan(std::move(launch_plan), + ServiceImpl::Factory{.runtime = plan.runtime}); + auto ret = session.run(std::move(session_plan)); js::on_finish(js::Tag{ .code = ret, @@ -116,7 +134,8 @@ void run(const core::Option::CatterOption& opt) { break; } default: { - throw std::runtime_error(std::format("UnExpected mode: {:0x}", static_cast(plan.mode))); + throw std::runtime_error( + std::format("UnExpected mode: {:0x}", static_cast(plan.mode))); } } } diff --git a/src/catter/core/session.h b/src/catter/core/session.h index e6851d2..a15e8ed 100644 --- a/src/catter/core/session.h +++ b/src/catter/core/session.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -15,10 +16,8 @@ #include "ipc.h" #include "util/data.h" -#include "util/crossplat.h" #include "util/eventide.h" #include "config/ipc.h" -#include "config/catter-proxy.h" namespace catter { @@ -41,14 +40,34 @@ class Session { public: using Acceptor = eventide::acceptor; + struct ProcessLaunchPlan { + std::string executable; + std::vector args; + }; + + template + struct RunPlan { + ProcessLaunchPlan launch_plan; + ServiceFactoryType factory; + }; + + template + requires ServiceFactoryLike> + static auto make_run_plan(ProcessLaunchPlan launch_plan, ServiceFactoryType&& factory) { + return RunPlan>{ + .launch_plan = std::move(launch_plan), + .factory = std::forward(factory), + }; + } + /** - * Run a session with the given shell and service factory. + * Run a session with the given launch plan and service factory. * @param factory The factory should be a callable that takes an ipcid_t and returns a * unique_ptr to a Service instance. */ template requires ServiceFactoryLike - int64_t run(const std::vector& shell, ServiceFactoryType&& factory) { + int64_t run(RunPlan run_plan) { #ifndef _WIN32 if(std::filesystem::exists(config::ipc::pipe_name())) { std::filesystem::remove(config::ipc::pipe_name()); @@ -65,24 +84,13 @@ class Session { this->acc = std::make_unique(std::move(*acc_ret)); - std::string executable; - std::vector args; - - using Result = ServiceFactoryResult; - if constexpr(std::convertible_to>) { - executable = (util::get_catter_root_path() / config::proxy::EXE_NAME).string(); - args = {executable, "-p", "0", "--exec", shell[0], "--"}; - append_range_to_vector(args, shell); - } else { - static_assert(false, "Unsupported service factory type"); - } - auto acceptor = [&](data::ipcid_t id, eventide::pipe&& client) -> eventide::task { - return ipc::accept(factory(id), std::move(client)); + return ipc::accept(run_plan.factory(id), std::move(client)); }; auto loop_task = this->loop(acceptor); - auto spawn_task = this->spawn(executable, args); + auto spawn_task = this->spawn(std::move(run_plan.launch_plan.executable), + std::move(run_plan.launch_plan.args)); default_loop().schedule(loop_task); default_loop().schedule(spawn_task); diff --git a/tests/integration/test/catter-proxy.cc b/tests/integration/test/catter-proxy.cc index 9d74cb6..fdae63d 100644 --- a/tests/integration/test/catter-proxy.cc +++ b/tests/integration/test/catter-proxy.cc @@ -94,8 +94,22 @@ int main(int argc, char* argv[]) { // catter::log::mute_logger(); try { Session session; + Session::ProcessLaunchPlan launch_plan; + launch_plan.executable = (util::get_catter_root_path() / config::proxy::EXE_NAME).string(); + launch_plan.args = { + launch_plan.executable, + "-p", + "0", + "--exec", + "echo", + "--", + "echo", + "Hello, World!", + }; + std::println("Session started."); - auto ret = session.run({"echo", "Hello, World!"}, ServiceImpl::Factory{}); + auto session_plan = Session::make_run_plan(std::move(launch_plan), ServiceImpl::Factory{}); + auto ret = session.run(std::move(session_plan)); std::println("Session finished with code: {}", ret); return 0; } catch(const std::exception& ex) { From 318c594e177682d90d1ee146f47f6391f46ae9af Mon Sep 17 00:00:00 2001 From: seele Date: Tue, 24 Mar 2026 18:12:56 +0800 Subject: [PATCH 4/5] refactor: enhance ServiceImpl factory methods with const correctness --- src/catter/core/app_runner.cc | 2 +- src/catter/core/session.cc | 35 +++++++++++-- src/catter/core/session.h | 70 ++++++++------------------ tests/integration/test/catter-proxy.cc | 2 +- 4 files changed, 54 insertions(+), 55 deletions(-) diff --git a/src/catter/core/app_runner.cc b/src/catter/core/app_runner.cc index f50699a..2f4cdf7 100644 --- a/src/catter/core/app_runner.cc +++ b/src/catter/core/app_runner.cc @@ -74,7 +74,7 @@ class ServiceImpl : public ipc::InjectService { struct Factory { const js::CatterRuntime* runtime; - std::unique_ptr operator() (data::ipcid_t id) { + std::unique_ptr operator() (data::ipcid_t id) const { return std::make_unique(id, runtime); } }; diff --git a/src/catter/core/session.cc b/src/catter/core/session.cc index 95e89fa..4f546ff 100644 --- a/src/catter/core/session.cc +++ b/src/catter/core/session.cc @@ -1,8 +1,5 @@ #include -#include -#include #include -#include #include #include #include @@ -18,8 +15,36 @@ namespace catter { -eventide::task Session::loop( - eventide::function_ref(data::ipcid_t, eventide::pipe&&)> acceptor) { +int64_t Session::run(RunPlan run_plan) { +#ifndef _WIN32 + if(std::filesystem::exists(config::ipc::pipe_name())) { + std::filesystem::remove(config::ipc::pipe_name()); + } +#endif + auto acc_ret = + eventide::pipe::listen(config::ipc::pipe_name(), eventide::pipe::options(), default_loop()); + + if(!acc_ret) { + throw std::runtime_error( + std::format("Failed to create acceptor: {}", acc_ret.error().message())); + } + + this->acc = std::make_unique(std::move(*acc_ret)); + + auto loop_task = this->loop(std::move(run_plan.callback)); + auto spawn_task = this->spawn(std::move(run_plan.launch_plan.executable), + std::move(run_plan.launch_plan.args)); + + default_loop().schedule(loop_task); + default_loop().schedule(spawn_task); + + default_loop().run(); + + loop_task.result(); // Propagate exceptions from loop task + return spawn_task.result(); +} + +eventide::task Session::loop(ClientAcceptor acceptor) { std::list> linked_clients; for(auto i: std::views::iota(data::ipcid_t(1))) { auto client = co_await this->acc->accept(); diff --git a/src/catter/core/session.h b/src/catter/core/session.h index a15e8ed..aac6dc9 100644 --- a/src/catter/core/session.h +++ b/src/catter/core/session.h @@ -16,8 +16,6 @@ #include "ipc.h" #include "util/data.h" -#include "util/eventide.h" -#include "config/ipc.h" namespace catter { @@ -38,76 +36,52 @@ concept ServiceFactoryLike = class Session { public: - using Acceptor = eventide::acceptor; + using PipeAcceptor = eventide::acceptor; + using ClientAcceptor = + eventide::function(data::ipcid_t, eventide::pipe&&)>; struct ProcessLaunchPlan { std::string executable; std::vector args; }; - template struct RunPlan { ProcessLaunchPlan launch_plan; - ServiceFactoryType factory; + ClientAcceptor callback; }; + /** + * Create a run plan with the given launch plan and client accepted callback. + * @param launch_plan The plan for launching the process. + * @param factory The factory for creating service instances when a client is accepted. + * @return A run plan containing the launch plan and client accepted callback. + */ template requires ServiceFactoryLike> static auto make_run_plan(ProcessLaunchPlan launch_plan, ServiceFactoryType&& factory) { - return RunPlan>{ + return RunPlan{ .launch_plan = std::move(launch_plan), - .factory = std::forward(factory), + .callback = + [factory = std::forward(factory)](data::ipcid_t id, + eventide::pipe&& client) { + return ipc::accept(factory(id), std::move(client)); + }, }; } /** - * Run a session with the given launch plan and service factory. - * @param factory The factory should be a callable that takes an ipcid_t and returns a - * unique_ptr to a Service instance. + * Run the session with the given run plan. + * @param run_plan The run plan containing the launch plan and service factory. + * @return The exit code of the spawned process. */ - template - requires ServiceFactoryLike - int64_t run(RunPlan run_plan) { -#ifndef _WIN32 - if(std::filesystem::exists(config::ipc::pipe_name())) { - std::filesystem::remove(config::ipc::pipe_name()); - } -#endif - auto acc_ret = eventide::pipe::listen(config::ipc::pipe_name(), - eventide::pipe::options(), - default_loop()); - - if(!acc_ret) { - throw std::runtime_error( - std::format("Failed to create acceptor: {}", acc_ret.error().message())); - } - - this->acc = std::make_unique(std::move(*acc_ret)); - - auto acceptor = [&](data::ipcid_t id, eventide::pipe&& client) -> eventide::task { - return ipc::accept(run_plan.factory(id), std::move(client)); - }; - - auto loop_task = this->loop(acceptor); - auto spawn_task = this->spawn(std::move(run_plan.launch_plan.executable), - std::move(run_plan.launch_plan.args)); - - default_loop().schedule(loop_task); - default_loop().schedule(spawn_task); - - default_loop().run(); - - loop_task.result(); // Propagate exceptions from loop task - return spawn_task.result(); - } + int64_t run(RunPlan run_plan); private: - eventide::task loop( - eventide::function_ref(data::ipcid_t, eventide::pipe&&)> acceptor); + eventide::task loop(ClientAcceptor acceptor); eventide::task spawn(std::string executable, std::vector args); - std::unique_ptr acc = nullptr; + std::unique_ptr acc = nullptr; }; } // namespace catter diff --git a/tests/integration/test/catter-proxy.cc b/tests/integration/test/catter-proxy.cc index fdae63d..06284bf 100644 --- a/tests/integration/test/catter-proxy.cc +++ b/tests/integration/test/catter-proxy.cc @@ -77,7 +77,7 @@ class ServiceImpl : public ipc::InjectService { } struct Factory { - std::unique_ptr operator() (data::ipcid_t id) { + std::unique_ptr operator() (data::ipcid_t id) const { return std::make_unique(id); } }; From 5adf0ab3eda2790cc725620b9be26a6e6ce11d62 Mon Sep 17 00:00:00 2001 From: seele Date: Tue, 24 Mar 2026 18:29:34 +0800 Subject: [PATCH 5/5] feat: refactor inject and execute_service functions to streamline session management --- src/catter/core/app_runner.cc | 81 +++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 38 deletions(-) diff --git a/src/catter/core/app_runner.cc b/src/catter/core/app_runner.cc index 2f4cdf7..1835f52 100644 --- a/src/catter/core/app_runner.cc +++ b/src/catter/core/app_runner.cc @@ -1,5 +1,6 @@ #include "app_runner.h" +#include #include #include #include @@ -7,6 +8,7 @@ #include #include "app_config.h" +#include "capi/type.h" #include "ipc.h" #include "js.h" #include "qjs.h" @@ -85,39 +87,38 @@ class ServiceImpl : public ipc::InjectService { const js::CatterRuntime* runtime = nullptr; }; -void inject(const RuntimePlan& plan) { - auto script_content = load_script_content(plan.script_path); - js::run_js_file(script_content, plan.script_path); - - auto new_config = js::on_start({ - .scriptPath = plan.script_path, - .scriptArgs = plan.script_args, - .buildSystemCommand = plan.build_system_command, - .runtime = *plan.runtime, - .options = {.log = plan.log}, - .isScriptSupported = true, - }); - - Session::ProcessLaunchPlan launch_plan; - launch_plan.executable = (util::get_catter_root_path() / config::proxy::EXE_NAME).string(); - launch_plan.args = { - launch_plan.executable, - "-p", - "0", - "--exec", - new_config.buildSystemCommand.front(), - "--", +int64_t inject(const js::CatterConfig& config) { + + auto proxy_path = util::get_catter_root_path() / config::proxy::EXE_NAME; + Session::ProcessLaunchPlan launch_plan{ + .executable = proxy_path.string(), + .args = + { + proxy_path.string(), + "-p", "0", + "--exec", config.buildSystemCommand.front(), + "--", }, }; - append_range_to_vector(launch_plan.args, new_config.buildSystemCommand); + append_range_to_vector(launch_plan.args, config.buildSystemCommand); Session session; auto session_plan = Session::make_run_plan(std::move(launch_plan), - ServiceImpl::Factory{.runtime = plan.runtime}); - auto ret = session.run(std::move(session_plan)); + ServiceImpl::Factory{.runtime = &config.runtime}); - js::on_finish(js::Tag{ - .code = ret, - }); + return session.run(std::move(session_plan)); +} + +int64_t execute_service(ipc::ServiceMode mode, const js::CatterConfig& config) { + switch(mode) { + case ipc::ServiceMode::INJECT: { + return inject(config); + } + default: { + throw std::runtime_error( + std::format("UnExpected mode: {:0x}", static_cast(mode))); + } + } + throw std::runtime_error("Not implemented"); } } // namespace @@ -125,19 +126,23 @@ void inject(const RuntimePlan& plan) { void run(const core::Option::CatterOption& opt) { auto startup = to_startup_config(opt); auto plan = build_runtime_plan(startup); + auto script_content = load_script_content(plan.script_path); js::init_qjs({.pwd = plan.working_dir}); + js::run_js_file(script_content, plan.script_path); - switch(plan.mode) { - case ipc::ServiceMode::INJECT: { - inject(plan); - break; - } - default: { - throw std::runtime_error( - std::format("UnExpected mode: {:0x}", static_cast(plan.mode))); - } - } + auto config = js::on_start({ + .scriptPath = plan.script_path, + .scriptArgs = plan.script_args, + .buildSystemCommand = plan.build_system_command, + .runtime = *plan.runtime, + .options = {.log = plan.log}, + .isScriptSupported = true, + }); + + js::on_finish(js::Tag{ + .code = execute_service(plan.mode, config), + }); } } // namespace catter::app