#include "editor/SpineOutputParser.hpp" #include namespace dts { namespace { std::optional firstVersion( const std::string& text, const std::regex& expression, std::string* trailing = nullptr) { std::smatch match; if (!std::regex_search(text, match, expression)) { return std::nullopt; } if (trailing && match.size() > 2) { *trailing = match[2].str(); } return SpineVersion::parse(match[1].str()); } } // namespace EditorProbeResult SpineOutputParser::parse( const std::filesystem::path& executable, const ProcessResult& process) const { EditorProbeResult result; result.executable = executable; result.timedOut = process.timedOut; result.exitCode = process.exitCode; result.output = process.output; if (!process.started) { result.status = EditorProbeStatus::LaunchFailed; result.message = process.error.empty() ? "Spine process could not be started." : process.error; return result; } static const std::regex launcher(R"(Spine Launcher\s+(\d+\.\d+(?:\.\d+)?))"); static const std::regex launching( R"((?:Launching:|Starting:)\s*Spine\s+(\d+\.\d+(?:\.\d+)?)(?:\s+([^\r\n]+))?)"); // MSVC's std::regex does not implement std::regex_constants::multiline, // so instead of relying on the '^' line anchor with the multiline flag, // search for "Spine " anywhere (regex_search scans the whole text). static const std::regex directEditor( R"(Spine\s+(\d+\.\d+(?:\.\d+)?)(?:\s+([^\r\n]+))?)"); result.launcherVersion = firstVersion(process.output, launcher); result.editorVersion = firstVersion(process.output, launching, &result.edition); if (!result.editorVersion) { result.editorVersion = firstVersion(process.output, directEditor, &result.edition); } if (!result.launcherVersion && !result.editorVersion) { result.status = EditorProbeStatus::UnrecognizedOutput; result.message = "Process started, but its output was not recognized as Spine."; } else if (process.timedOut) { result.status = EditorProbeStatus::ObservedButTimedOut; result.message = "Spine was detected, but the version probe timed out."; } else if (process.exitCode == 0) { result.status = EditorProbeStatus::Available; result.message = "Spine CLI probe succeeded."; } else { result.status = EditorProbeStatus::LaunchFailed; result.message = "Spine was detected, but the process returned a failure code."; } return result; } std::string toString(EditorProbeStatus status) { switch (status) { case EditorProbeStatus::Available: return "available"; case EditorProbeStatus::ObservedButTimedOut: return "observed-but-timed-out"; case EditorProbeStatus::PathMissing: return "path-missing"; case EditorProbeStatus::LaunchFailed: return "launch-failed"; case EditorProbeStatus::UnrecognizedOutput: return "unrecognized-output"; } return "unknown"; } } // namespace dts