/** * * @file ReplyCellRunner.cpp * @author Gaspard Kirira * * Copyright 2026, Gaspard Kirira. * All rights reserved. * https://github.com/vixcpp/note * * Use of this source code is governed by a MIT license * that can be found in the LICENSE file. * * Vix Note * */ #include #include #include namespace vix::note { namespace { std::string strip_reply_line_comments(const std::string &source) { std::string normalized; normalized.reserve(source.size()); bool inString = false; bool escaped = false; for (std::size_t index = 0; index < source.size(); ++index) { const char current = source[index]; if (inString) { normalized += current; if (escaped) { escaped = false; } else if (current == '\\') { escaped = true; } else if (current == '"') { inString = false; } continue; } if (current == '"') { inString = true; normalized += current; continue; } if (current == '/' && index + 1 < source.size() && source[index + 1] == '/') { while (index < source.size() && source[index] != '\n') { ++index; } if (index < source.size()) { normalized += '\n'; } continue; } normalized += current; } return normalized; } void add_reply_outputs( NoteResult &result, const ReplyCellRunnerOptions &options, const vix::reply::ReplyRunResult &replyResult) { if (!replyResult.stdoutText.empty()) { result.add_stdout(replyResult.stdoutText); } if (!replyResult.stderrText.empty()) { result.add_stderr(replyResult.stderrText); } if (!replyResult.errorText.empty()) { result.add_error(replyResult.errorText); } if (options.debugMode) { result.add_debug("duration_ms=" + std::to_string(replyResult.elapsedMs)); result.add_debug("exit_code=" + std::to_string(replyResult.exitCode)); } } } ReplyCellRunner::ReplyCellRunner() : runtime_(options_.runtimeOptions) { runtime_.set_args(options_.args); } ReplyCellRunner::ReplyCellRunner(ReplyCellRunnerOptions options) : options_(std::move(options)), runtime_(options_.runtimeOptions) { runtime_.set_args(options_.args); } const ReplyCellRunnerOptions &ReplyCellRunner::options() const noexcept { return options_; } void ReplyCellRunner::set_options(ReplyCellRunnerOptions options) { options_ = std::move(options); reset_runtime(); } NoteResult ReplyCellRunner::run_source(const std::string &source) { if (source.empty()) { return NoteResult::failure("empty Reply cell", 1) .add_error("empty Reply cell"); } vix::reply::ReplyRunResult replyResult = runtime_.run_cell(strip_reply_line_comments(source)); NoteResult result = replyResult.ok ? NoteResult::success("Reply cell executed") : NoteResult::failure("Reply cell failed", replyResult.exitCode == 0 ? 1 : replyResult.exitCode); add_reply_outputs(result, options_, replyResult); return result; } NoteResult ReplyCellRunner::run_cell(const NoteCell &cell) { if (cell.kind() != NoteCellKind::Reply) { return NoteResult::failure("cell is not a Reply cell", 1) .add_error("cell is not a Reply cell"); } return run_source(cell.source()); } void ReplyCellRunner::clear() { runtime_.clear(); } void ReplyCellRunner::reset_runtime() { runtime_ = vix::reply::ReplyRuntime(options_.runtimeOptions); runtime_.set_args(options_.args); } NoteResult run_reply_source(const std::string &source) { return ReplyCellRunner().run_source(source); } NoteResult run_reply_cell(const NoteCell &cell) { return ReplyCellRunner().run_cell(cell); } }