#pragma once #include #include #include namespace mcc::utils { static const std::regex decimalNumberRx{" *[-+]?([0-9]*[.])?[0-9]+([eE][-+]?[0-9]+)? *"}; static const std::regex sexagesimalReprRx{" *[-+]?[0-9]{1,2}:[0-9]{1,2}:([0-9]{0,2}[.])?[0-9]+ *"}; enum class TrimType { TRIM_LEFT, TRIM_RIGHT, TRIM_BOTH }; template static std::string_view trimSpaces(R&& r, TrimType type = TrimType::TRIM_BOTH) requires std::same_as>> { auto is_space = [](const auto& ch) { return ch == ' '; }; auto end = std::forward(r).end(); auto f1 = std::forward(r).begin(); if (type != TrimType::TRIM_RIGHT) { // look for the first non-space symbol f1 = std::ranges::find_if_not(std::forward(r), is_space); if (f1 == end) { // all are spaces! return std::string_view(); } } auto f2 = end; if (type != TrimType::TRIM_LEFT) { auto f3 = f1; do { f2 = std::ranges::find_if(++f3, end, is_space); if (f2 == end) break; f3 = std::ranges::find_if_not(f2 + 1, end, is_space); } while (f3 != end); } return std::string_view(f1, f2); } template std::optional numFromStr(R&& r) requires((std::integral || std::floating_point) && std::same_as>>) { T val; const char* end_ptr = &*r.end(); if constexpr (std::integral) { auto cvt_res = std::from_chars(&*r.begin(), &*r.end(), val); if (cvt_res.ec != std::errc()) { return std::nullopt; } else if (cvt_res.ptr != end_ptr) { return std::nullopt; } } else { #ifdef _LIBCPP_VERSION // clang's libc++ does not have floating-point overloads for std::from_chars std::string s{str.begin(), str.end()}; size_t pos; try { if constexpr (std::same_as) { val = std::stof(s, &pos); } else if constexpr (std::same_as) { val = std::stod(s, &pos); } else { val = std::stold(s, &pos); } } catch (...) { return std::nullopt; } if (pos != s.size()) { return std::nullopt; } #else auto cvt_res = std::from_chars(&*r.begin(), &*r.end(), val); if (cvt_res.ec != std::errc()) { return std::nullopt; } else if (cvt_res.ptr != end_ptr) { return std::nullopt; } #endif } return val; } template static std::optional parsAngleString(R&& r, bool hms = false) requires std::same_as>> { auto str = trimSpaces(std::forward(r)); bool ok = std::regex_match(str.begin(), str.end(), decimalNumberRx); if (ok) { return numFromStr(str); } ok = std::regex_match(str.begin(), str.end(), sexagesimalReprRx); if (ok) { auto str = trimSpaces(std::forward(r)); auto parts = std::views::split(str, std::string_view(":")); double val; double p1 = numFromStr(*parts.begin()).value(); val = std::abs(p1); double dv = 60.0; for (auto const& v : parts | std::views::drop(1)) { val += numFromStr(v).value() / dv; dv *= 60.0; } // val += numFromStr(parts[1]) / 60.0; // val += numFromStr(parts[2]) / 3600.0; if (p1 < 0) { val = -val; } if (hms) { val *= 15.0; } return val; } return std::nullopt; } } // namespace mcc::utils