/* * Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved. */ module; #include export module hull; namespace hull { export template constexpr ox::Result> parseCmd(ox::StringViewCR cmd) noexcept requires(ox::is_same_v || ox::is_same_v) { auto const tokens = split(cmd, ' '); ox::Vector args; char waitingFor{}; auto const handleString = [&waitingFor, &args]( ox::StringViewCR token, char const delimiter) { if (endsWith(token, delimiter)) { args.emplace_back(substr(token, 1, token.size() - 1)); } else { waitingFor = delimiter; args.emplace_back(substr(token, 1)); } }; for (auto const &token : tokens) { if (waitingFor) { if (endsWith(token, waitingFor)) { waitingFor = 0; } auto &tgt = *args.back().value; if constexpr (ox::is_same_v) { tgt += substr(token, 0, token.size() - 1); } else { tgt = {tgt.data(), tgt.size() + token.size() - 1}; } } else if (beginsWith(token, '"')) { handleString(token, '"'); } else if (beginsWith(token, '\'')) { handleString(token, '\''); } else { args.emplace_back(token); } } if (waitingFor) { return ox::Error{1, "unterminated string"}; } return args; } template [[nodiscard]] static constexpr bool testParse(ox::StringViewCR cmd, std::initializer_list const &expected) noexcept { auto const [args, err] = parseCmd(cmd); static constexpr auto equals = [](auto const &a, auto const &b) { if (a.size() != b.size()) { return false; } for (auto i = 0u; i < a.size(); ++i) { if (a[i] != b[i]) { return false; } } return true; }; return !err && equals(args, ox::Vector(expected)); } static_assert(testParse("echo asdf", {"echo", "asdf"})); static_assert(testParse("echo asdf", {"echo", "asdf"})); static_assert(testParse("echo \"asdf\"", {"echo", "asdf"})); static_assert(testParse("echo \"asdf\"", {"echo", "asdf"})); static_assert(testParse("echo 'asdf'", {"echo", "asdf"})); static_assert(testParse("echo 'asdf'", {"echo", "asdf"})); static_assert(testParse("echo 'asdf' aoue", {"echo", "asdf", "aoue"})); static_assert(testParse("echo 'asdf' aoue", {"echo", "asdf", "aoue"})); export class Prompt { private: ox::String m_cmd; ox::String m_workingDir{"/"}; ox::Vector m_prevCmds; public: private: }; }