diff --git a/.travis.yml b/.travis.yml index 71b7715..a7ad922 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,6 +30,6 @@ addons: - "libqt5charts5-dev" script: - - "qmake CLONE_CPPUTILS=1 CLONE_DATE=1 test/tstespchrono.pro" + - "qmake CLONE_CPPUTILS=1 CLONE_DATE=1 CLONE_EXPECTED=1 CLONE_FMT=1 test/tstespchrono.pro" - "make -j2" - ./tstespchrono diff --git a/CMakeLists.txt b/CMakeLists.txt index f028603..17f9310 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,8 +10,11 @@ set(sources set(dependencies freertos esp_system + cpputils date + expected + fmt ) idf_component_register( diff --git a/src/espchrono.cpp b/src/espchrono.cpp index 5633cdd..d8dce40 100644 --- a/src/espchrono.cpp +++ b/src/espchrono.cpp @@ -1,5 +1,8 @@ #include "espchrono.h" +// 3rdparty lib includes +#include + using namespace std::chrono_literals; using namespace date; @@ -176,7 +179,7 @@ LocalDateTime toDateTime(local_clock::time_point ts) return dateTime; } -std::optional parseDateTime(std::string_view str) +tl::expected parseDateTime(std::string_view str) { // both valid: // 2020-11-10T21:31 @@ -193,7 +196,7 @@ std::optional parseDateTime(std::string_view str) constexpr auto dateTimeFormat = "%4d-%2u-%2uT%2hhu:%2hhu:%2hhu.%3hu"; if (const auto scanned = std::sscanf(str.data(), dateTimeFormat, &year, &month, &day, &hour, &minute, &second, &millisecond); scanned < 5) - return std::nullopt; + return tl::make_unexpected(fmt::format("invalid DateTime ({})", str)); return DateTime{ .date=date::year_month_day{date::year{year}, date::month{month}, date::day{day}}, @@ -204,51 +207,46 @@ std::optional parseDateTime(std::string_view str) }; } -std::optional parseDaypoint(std::string_view str) +tl::expected parseDaypoint(std::string_view str) { int8_t hour, minute, second{}; constexpr auto daypointFormat = "%2hhd:%2hhd:%2hhd"; if (const auto scanned = std::sscanf(str.data(), daypointFormat, &hour, &minute, &second); scanned < 2) - return std::nullopt; + return tl::make_unexpected(fmt::format("invalid daypoint ({})", str)); if (hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > 59) - return std::nullopt; + return tl::make_unexpected(fmt::format("invalid daypoint ({})", str)); return std::chrono::hours{hour} + std::chrono::minutes{minute} + std::chrono::seconds{second}; } std::string toString(const DateTime &dateTime) { - char buf[31]; - - std::sprintf(buf, "%04i-%02u-%02uT%02hhu:%02hhu:%02hhu.%03hu", - int{dateTime.date.year()}, unsigned{dateTime.date.month()}, unsigned{dateTime.date.day()}, - dateTime.hour, dateTime.minute, dateTime.second, dateTime.millisecond); - - return std::string{buf}; + return fmt::format("{:04}-{:02}-{:02}T{:02}:{:02}:{:02}.{:03}", + int{dateTime.date.year()}, unsigned{dateTime.date.month()}, unsigned{dateTime.date.day()}, + dateTime.hour, dateTime.minute, dateTime.second, dateTime.millisecond); } std::string toString(const LocalDateTime &dateTime) { - char buf[35]; - date::hh_mm_ss helper{dateTime.timezone.offset + hours32{dateTime.dst ? 1 : 0}}; - std::sprintf(buf, "%04i-%02u-%02uT%02hhu:%02hhu:%02hhu.%03hu %s%02hhu:%02hhu", - int{dateTime.date.year()}, unsigned{dateTime.date.month()}, unsigned{dateTime.date.day()}, - dateTime.hour, dateTime.minute, dateTime.second, dateTime.millisecond, - helper.is_negative() ? "-" : "+", uint8_t(helper.hours().count()), uint8_t(helper.minutes().count())); - - return std::string{buf}; + return fmt::format("{:04}-{:02}-{:02}T{:02}:{:02}:{:02}.{:03} {}{:02}:{:02}", + int{dateTime.date.year()}, unsigned{dateTime.date.month()}, unsigned{dateTime.date.day()}, + dateTime.hour, dateTime.minute, dateTime.second, dateTime.millisecond, + helper.is_negative() ? "-" : "+", uint8_t(helper.hours().count()), uint8_t(helper.minutes().count())); } std::string toDaypointString(std::chrono::seconds seconds) { date::hh_mm_ss helper(seconds); - char buf[10]; - std::sprintf(buf, "%s%02hhd:%02hhd:%02hhd", helper.is_negative() ? "-" : "", int8_t(helper.hours().count()), int8_t(helper.minutes().count()), int8_t(helper.seconds().count())); - return std::string{buf}; + + return fmt::format("{}{:02}:{:02}:{:02}", + helper.is_negative() ? "-" : "", + helper.hours().count(), + helper.minutes().count(), + helper.seconds().count()); } std::chrono::milliseconds ago(millis_clock::time_point a) @@ -256,10 +254,10 @@ std::chrono::milliseconds ago(millis_clock::time_point a) return millis_clock::now() - a; } -std::string toString(milliseconds32 val) { return std::to_string(val.count()) + "ms"; } -std::string toString(seconds32 val) { return std::to_string(val.count()) + "s"; } -std::string toString(minutes32 val) { return std::to_string(val.count()) + "min"; } -std::string toString(hours32 val) { return std::to_string(val.count()) + "h"; } +std::string toString(milliseconds32 val) { return fmt::format("{}ms", val.count()); } +std::string toString(seconds32 val) { return fmt::format("{}s", val.count()); } +std::string toString(minutes32 val) { return fmt::format("{}min", val.count()); } +std::string toString(hours32 val) { return fmt::format("{}h", val.count()); } IMPLEMENT_TYPESAFE_ENUM(DayLightSavingMode, : uint8_t, DayLightSavingModeValues) diff --git a/src/espchrono.h b/src/espchrono.h index 2569beb..e09b89d 100644 --- a/src/espchrono.h +++ b/src/espchrono.h @@ -5,12 +5,12 @@ #include #include #include -#include #include #include // 3rdparty lib includes #include +#include // local includes #include "cpptypesafeenum.h" @@ -171,11 +171,9 @@ local_clock::time_point utcToLocal(utc_clock::time_point ts); DateTime toDateTime(utc_clock::time_point ts); LocalDateTime toDateTime(local_clock::time_point ts); -//! Returns null if string cannot be parsed -std::optional parseDateTime(std::string_view str); +tl::expected parseDateTime(std::string_view str); -//! Returns null if string cannot be parsed -std::optional parseDaypoint(std::string_view str); +tl::expected parseDaypoint(std::string_view str); std::string toString(const DateTime &dateTime); diff --git a/test/.gitignore b/test/.gitignore index f616324..a469fff 100644 --- a/test/.gitignore +++ b/test/.gitignore @@ -1,3 +1,5 @@ *.user* cpputils/ date/ +expected/ +fmt/ diff --git a/test/espchronotestutils.cpp b/test/espchronotestutils.cpp index b8a5d61..e557f77 100644 --- a/test/espchronotestutils.cpp +++ b/test/espchronotestutils.cpp @@ -40,7 +40,7 @@ char *toString(const espchrono::seconds32 &val) } template<> -char *toString(const std::optional &val) +char *toString(const tl::expected &val) { return val ? ::QTest::toString(*val) : ::QTest::toString("(invalid)"); } diff --git a/test/espchronotestutils.h b/test/espchronotestutils.h index d7a1183..14f3421 100644 --- a/test/espchronotestutils.h +++ b/test/espchronotestutils.h @@ -23,7 +23,8 @@ Q_DECLARE_METATYPE(espchrono::utc_clock::time_point) Q_DECLARE_METATYPE(espchrono::local_clock::time_point) Q_DECLARE_METATYPE(espchrono::DateTime) Q_DECLARE_METATYPE(espchrono::LocalDateTime) -Q_DECLARE_METATYPE(std::optional) +using X = tl::expected; +Q_DECLARE_METATYPE(X) extern const espchrono::time_zone testTimeZone; @@ -64,5 +65,5 @@ template<> char *toString(const espchrono::seconds32 &val); template<> -char *toString(const std::optional &val); +char *toString(const tl::expected &val); } // namespace QTest diff --git a/test/tst_espchrono.cpp b/test/tst_espchrono.cpp index 2d051fd..1f98d13 100644 --- a/test/tst_espchrono.cpp +++ b/test/tst_espchrono.cpp @@ -227,23 +227,27 @@ private slots: void test_parseDaypoint_data() { - QTest::addColumn("input"); - QTest::addColumn>("expected"); + using result_t = tl::expected; - QTest::addRow("bullshit") << "bullshit"s << std::optional{}; - QTest::addRow("missing_minute") << "00:"s << std::optional{}; - QTest::addRow("zero") << "00:00"s << std::optional{0s}; - QTest::addRow("zero3") << "00:00:00"s << std::optional{0s}; - QTest::addRow("random") << "12:34:56"s << std::optional{12h+34min+56s}; - QTest::addRow("random2") << "12:34"s << std::optional{12h+34min}; -// QTest::addRow("negative") << "-12:34:56"s << std::optional{-12h-34min-56s}; -// QTest::addRow("negative_leading_zero") << "-00:34:56"s << std::optional{-34min-56s}; + QTest::addColumn("input"); + QTest::addColumn("expected"); + + QTest::addRow("bullshit") << "bullshit"s << result_t{tl::make_unexpected("invalid daypoint (bullshit)")}; + QTest::addRow("missing_minute") << "00:"s << result_t{tl::make_unexpected("invalid daypoint (00:)")}; + QTest::addRow("zero") << "00:00"s << result_t{0s}; + QTest::addRow("zero3") << "00:00:00"s << result_t{0s}; + QTest::addRow("random") << "12:34:56"s << result_t{12h+34min+56s}; + QTest::addRow("random2") << "12:34"s << result_t{12h+34min}; +// QTest::addRow("negative") << "-12:34:56"s << result_t{-12h-34min-56s}; +// QTest::addRow("negative_leading_zero") << "-00:34:56"s << result_t{-34min-56s}; } void test_parseDaypoint() { + using result_t = tl::expected; + QFETCH(std::string, input); - QFETCH(std::optional, expected); + QFETCH(result_t, expected); FIXEDCOMPARE(espchrono::parseDaypoint(input), expected); } diff --git a/test/tstespchrono.pro b/test/tstespchrono.pro index 3f10c9f..9a535ef 100644 --- a/test/tstespchrono.pro +++ b/test/tstespchrono.pro @@ -53,4 +53,39 @@ equals(CLONE_DATE, 1) { include($$DATE_DIR/date.pri) +equals(CLONE_EXPECTED, 1) { + EXPECTED_DIR = $$PWD/expected + + message("Checking out expected...") + exists($$EXPECTED_DIR/.git): { + system("git -C $$EXPECTED_DIR pull") + } else { + system("git clone https://github.com/0xFEEDC0DE64/expected.git $$EXPECTED_DIR") + } +} else: exists($$PWD/../../expected/include) { + EXPECTED_DIR = $$PWD/../../expected +} else { + error("expected not found, please run git submodule update --init") +} + +include($$EXPECTED_DIR/expected.pri) + +equals(CLONE_FMT, 1) { + FMT_DIR = $$PWD/fmt + + message("Checking out fmt...") + exists($$FMT_DIR/.git): { + system("git -C $$FMT_DIR pull") + } else { + system("git clone https://github.com/0xFEEDC0DE64/fmt.git $$FMT_DIR") + } +} else: exists($$PWD/../../fmt/include) { + FMT_DIR = $$PWD/../../fmt +} else { + error("fmt not found, please run git submodule update --init") +} + +include($$FMT_DIR/fmt.pri) +include($$FMT_DIR/fmt_src.pri) + QMAKE_CXXFLAGS += -Wno-missing-field-initializers