/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2019 Danny Robson */ #include "parse/time.hpp" #include "parse/value.hpp" #include "../ascii.hpp" #include // CXX#20: Define these ratios that aren't currently in all our // implementations of the stdlib. using days = std::chrono::duration>; using weeks = std::chrono::duration>; using months = std::chrono::duration>; using years = std::chrono::duration>; /////////////////////////////////////////////////////////////////////////////// /// Shorthand for converting an arbitrary duration to nanoseconds. template std::chrono::nanoseconds to_ns (DurationT val) { return std::chrono::duration_cast (val); } /////////////////////////////////////////////////////////////////////////////// enum plural_t { PLURAL, SINGULAR }; ///---------------------------------------------------------------------------- /// Try to consume a prefix of a string view, optionally allowing for /// pluralisation. /// /// If the prefix is detected the provided view is modified to point /// immediately past the prefix, and the value true is returned. Else, the view /// is untouched and false is returned. /// /// \tparam _N The number of characters in the null-terminated prefix. /// \tparam N The number of displayable characters in the prefix. /// \param str The view to be checked for the prefix. /// \param prefix The prefix we are checking for. /// \param plural Whether to (optionall) perform pluralisation on the prefix. /// \return True IFF the prefix was found and the view was updated. template static bool try_consume_prefix ( cruft::view &str, char const (&prefix)[N_], plural_t plural = SINGULAR) { static_assert (N > 0); // Ensure both sequences have the same prefix if (str.size () < N) return false; for (size_t i = 0; i < N; ++i) if (str[i] != prefix[i]) return false; // Eat the prefix into a temporary auto res = str.consume (N); // Each the plural suffix if we support one if (plural == PLURAL && res.size () >= 1 && res[0] == 's') res = res.consume (1); // If, after the above, we are empty or we have space then we have been // successful... if (res.empty () || cruft::ascii::is_space (str[0])) { str = res; return true; } // ... otherwise we probably have a trailing unprocessed suffix; // eg, 's' with a trailing 'econds' return false; } /////////////////////////////////////////////////////////////////////////////// cruft::expected cruft::parse::duration::consume (cruft::view &remain) { auto const count = value (remain); if (remain.empty ()) return to_ns (std::chrono::seconds (count)); while (remain[0] == ' ') { remain = remain.consume (1); if (remain.empty ()) return to_ns (std::chrono::seconds (count)); } if (try_consume_prefix (remain, "ns")) return std::chrono::nanoseconds (count); if (try_consume_prefix (remain, "us")) return to_ns (std::chrono::microseconds (count)); if (try_consume_prefix (remain, "ms")) return to_ns (std::chrono::milliseconds (count)); if (try_consume_prefix (remain, "s")) return to_ns (std::chrono::seconds (count)); if (try_consume_prefix (remain, "m")) return to_ns (std::chrono::minutes (count)); if (try_consume_prefix (remain, "h")) return to_ns (std::chrono::hours (count)); if (try_consume_prefix (remain, "second", PLURAL)) return to_ns (std::chrono::seconds (count)); if (try_consume_prefix (remain, "minute", PLURAL)) return to_ns (std::chrono::minutes (count)); if (try_consume_prefix (remain, "hour", PLURAL)) return to_ns (std::chrono::hours (count)); if (try_consume_prefix (remain, "day", PLURAL)) return to_ns (days (count)); if (try_consume_prefix (remain, "week", PLURAL)) return to_ns (weeks (count)); if (try_consume_prefix (remain, "month", PLURAL)) return to_ns (months (count)); if (try_consume_prefix (remain, "year", PLURAL)) return to_ns (years (count)); return cruft::unexpected (std::errc::invalid_argument); } //----------------------------------------------------------------------------- cruft::expected cruft::parse::duration::from (cruft::view const &str) { auto remain = str; // Try to parse from the temporary view. auto res = ::cruft::parse::duration::consume (remain); if (!res) return res; // Ensure it was totally consumed. if (!remain.empty ()) return cruft::unexpected (std::errc::invalid_argument); return res; }