added expected and fmt for cleaner code/interfaces
This commit is contained in:
@ -30,6 +30,6 @@ addons:
|
|||||||
- "libqt5charts5-dev"
|
- "libqt5charts5-dev"
|
||||||
|
|
||||||
script:
|
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"
|
- "make -j2"
|
||||||
- ./tstespchrono
|
- ./tstespchrono
|
||||||
|
@ -10,8 +10,11 @@ set(sources
|
|||||||
set(dependencies
|
set(dependencies
|
||||||
freertos
|
freertos
|
||||||
esp_system
|
esp_system
|
||||||
|
|
||||||
cpputils
|
cpputils
|
||||||
date
|
date
|
||||||
|
expected
|
||||||
|
fmt
|
||||||
)
|
)
|
||||||
|
|
||||||
idf_component_register(
|
idf_component_register(
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
#include "espchrono.h"
|
#include "espchrono.h"
|
||||||
|
|
||||||
|
// 3rdparty lib includes
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
using namespace date;
|
using namespace date;
|
||||||
|
|
||||||
@ -176,7 +179,7 @@ LocalDateTime toDateTime(local_clock::time_point ts)
|
|||||||
return dateTime;
|
return dateTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<DateTime> parseDateTime(std::string_view str)
|
tl::expected<DateTime, std::string> parseDateTime(std::string_view str)
|
||||||
{
|
{
|
||||||
// both valid:
|
// both valid:
|
||||||
// 2020-11-10T21:31
|
// 2020-11-10T21:31
|
||||||
@ -193,7 +196,7 @@ std::optional<DateTime> parseDateTime(std::string_view str)
|
|||||||
|
|
||||||
constexpr auto dateTimeFormat = "%4d-%2u-%2uT%2hhu:%2hhu:%2hhu.%3hu";
|
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)
|
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{
|
return DateTime{
|
||||||
.date=date::year_month_day{date::year{year}, date::month{month}, date::day{day}},
|
.date=date::year_month_day{date::year{year}, date::month{month}, date::day{day}},
|
||||||
@ -204,51 +207,46 @@ std::optional<DateTime> parseDateTime(std::string_view str)
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::chrono::seconds> parseDaypoint(std::string_view str)
|
tl::expected<std::chrono::seconds, std::string> parseDaypoint(std::string_view str)
|
||||||
{
|
{
|
||||||
int8_t hour, minute, second{};
|
int8_t hour, minute, second{};
|
||||||
|
|
||||||
constexpr auto daypointFormat = "%2hhd:%2hhd:%2hhd";
|
constexpr auto daypointFormat = "%2hhd:%2hhd:%2hhd";
|
||||||
if (const auto scanned = std::sscanf(str.data(), daypointFormat, &hour, &minute, &second); scanned < 2)
|
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)
|
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};
|
return std::chrono::hours{hour} + std::chrono::minutes{minute} + std::chrono::seconds{second};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string toString(const DateTime &dateTime)
|
std::string toString(const DateTime &dateTime)
|
||||||
{
|
{
|
||||||
char buf[31];
|
return fmt::format("{:04}-{:02}-{:02}T{:02}:{:02}:{:02}.{:03}",
|
||||||
|
int{dateTime.date.year()}, unsigned{dateTime.date.month()}, unsigned{dateTime.date.day()},
|
||||||
std::sprintf(buf, "%04i-%02u-%02uT%02hhu:%02hhu:%02hhu.%03hu",
|
dateTime.hour, dateTime.minute, dateTime.second, dateTime.millisecond);
|
||||||
int{dateTime.date.year()}, unsigned{dateTime.date.month()}, unsigned{dateTime.date.day()},
|
|
||||||
dateTime.hour, dateTime.minute, dateTime.second, dateTime.millisecond);
|
|
||||||
|
|
||||||
return std::string{buf};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string toString(const LocalDateTime &dateTime)
|
std::string toString(const LocalDateTime &dateTime)
|
||||||
{
|
{
|
||||||
char buf[35];
|
|
||||||
|
|
||||||
date::hh_mm_ss helper{dateTime.timezone.offset + hours32{dateTime.dst ? 1 : 0}};
|
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",
|
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()},
|
int{dateTime.date.year()}, unsigned{dateTime.date.month()}, unsigned{dateTime.date.day()},
|
||||||
dateTime.hour, dateTime.minute, dateTime.second, dateTime.millisecond,
|
dateTime.hour, dateTime.minute, dateTime.second, dateTime.millisecond,
|
||||||
helper.is_negative() ? "-" : "+", uint8_t(helper.hours().count()), uint8_t(helper.minutes().count()));
|
helper.is_negative() ? "-" : "+", uint8_t(helper.hours().count()), uint8_t(helper.minutes().count()));
|
||||||
|
|
||||||
return std::string{buf};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string toDaypointString(std::chrono::seconds seconds)
|
std::string toDaypointString(std::chrono::seconds seconds)
|
||||||
{
|
{
|
||||||
date::hh_mm_ss helper(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 fmt::format("{}{:02}:{:02}:{:02}",
|
||||||
return std::string{buf};
|
helper.is_negative() ? "-" : "",
|
||||||
|
helper.hours().count(),
|
||||||
|
helper.minutes().count(),
|
||||||
|
helper.seconds().count());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::chrono::milliseconds ago(millis_clock::time_point a)
|
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;
|
return millis_clock::now() - a;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string toString(milliseconds32 val) { return std::to_string(val.count()) + "ms"; }
|
std::string toString(milliseconds32 val) { return fmt::format("{}ms", val.count()); }
|
||||||
std::string toString(seconds32 val) { return std::to_string(val.count()) + "s"; }
|
std::string toString(seconds32 val) { return fmt::format("{}s", val.count()); }
|
||||||
std::string toString(minutes32 val) { return std::to_string(val.count()) + "min"; }
|
std::string toString(minutes32 val) { return fmt::format("{}min", val.count()); }
|
||||||
std::string toString(hours32 val) { return std::to_string(val.count()) + "h"; }
|
std::string toString(hours32 val) { return fmt::format("{}h", val.count()); }
|
||||||
|
|
||||||
IMPLEMENT_TYPESAFE_ENUM(DayLightSavingMode, : uint8_t, DayLightSavingModeValues)
|
IMPLEMENT_TYPESAFE_ENUM(DayLightSavingMode, : uint8_t, DayLightSavingModeValues)
|
||||||
|
|
||||||
|
@ -5,12 +5,12 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <ratio>
|
#include <ratio>
|
||||||
#include <optional>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
// 3rdparty lib includes
|
// 3rdparty lib includes
|
||||||
#include <date/date.h>
|
#include <date/date.h>
|
||||||
|
#include <tl/expected.hpp>
|
||||||
|
|
||||||
// local includes
|
// local includes
|
||||||
#include "cpptypesafeenum.h"
|
#include "cpptypesafeenum.h"
|
||||||
@ -171,11 +171,9 @@ local_clock::time_point utcToLocal(utc_clock::time_point ts);
|
|||||||
DateTime toDateTime(utc_clock::time_point ts);
|
DateTime toDateTime(utc_clock::time_point ts);
|
||||||
LocalDateTime toDateTime(local_clock::time_point ts);
|
LocalDateTime toDateTime(local_clock::time_point ts);
|
||||||
|
|
||||||
//! Returns null if string cannot be parsed
|
tl::expected<DateTime, std::string> parseDateTime(std::string_view str);
|
||||||
std::optional<DateTime> parseDateTime(std::string_view str);
|
|
||||||
|
|
||||||
//! Returns null if string cannot be parsed
|
tl::expected<std::chrono::seconds, std::string> parseDaypoint(std::string_view str);
|
||||||
std::optional<std::chrono::seconds> parseDaypoint(std::string_view str);
|
|
||||||
|
|
||||||
std::string toString(const DateTime &dateTime);
|
std::string toString(const DateTime &dateTime);
|
||||||
|
|
||||||
|
2
test/.gitignore
vendored
2
test/.gitignore
vendored
@ -1,3 +1,5 @@
|
|||||||
*.user*
|
*.user*
|
||||||
cpputils/
|
cpputils/
|
||||||
date/
|
date/
|
||||||
|
expected/
|
||||||
|
fmt/
|
||||||
|
@ -40,7 +40,7 @@ char *toString(const espchrono::seconds32 &val)
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
char *toString(const std::optional<espchrono::seconds32> &val)
|
char *toString(const tl::expected<espchrono::seconds32, std::string> &val)
|
||||||
{
|
{
|
||||||
return val ? ::QTest::toString(*val) : ::QTest::toString("(invalid)");
|
return val ? ::QTest::toString(*val) : ::QTest::toString("(invalid)");
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,8 @@ Q_DECLARE_METATYPE(espchrono::utc_clock::time_point)
|
|||||||
Q_DECLARE_METATYPE(espchrono::local_clock::time_point)
|
Q_DECLARE_METATYPE(espchrono::local_clock::time_point)
|
||||||
Q_DECLARE_METATYPE(espchrono::DateTime)
|
Q_DECLARE_METATYPE(espchrono::DateTime)
|
||||||
Q_DECLARE_METATYPE(espchrono::LocalDateTime)
|
Q_DECLARE_METATYPE(espchrono::LocalDateTime)
|
||||||
Q_DECLARE_METATYPE(std::optional<espchrono::seconds32>)
|
using X = tl::expected<espchrono::seconds32, std::string>;
|
||||||
|
Q_DECLARE_METATYPE(X)
|
||||||
|
|
||||||
extern const espchrono::time_zone testTimeZone;
|
extern const espchrono::time_zone testTimeZone;
|
||||||
|
|
||||||
@ -64,5 +65,5 @@ template<>
|
|||||||
char *toString(const espchrono::seconds32 &val);
|
char *toString(const espchrono::seconds32 &val);
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
char *toString(const std::optional<espchrono::seconds32> &val);
|
char *toString(const tl::expected<espchrono::seconds32, std::string> &val);
|
||||||
} // namespace QTest
|
} // namespace QTest
|
||||||
|
@ -227,23 +227,27 @@ private slots:
|
|||||||
|
|
||||||
void test_parseDaypoint_data()
|
void test_parseDaypoint_data()
|
||||||
{
|
{
|
||||||
QTest::addColumn<std::string>("input");
|
using result_t = tl::expected<espchrono::seconds32, std::string>;
|
||||||
QTest::addColumn<std::optional<espchrono::seconds32>>("expected");
|
|
||||||
|
|
||||||
QTest::addRow("bullshit") << "bullshit"s << std::optional<espchrono::seconds32>{};
|
QTest::addColumn<std::string>("input");
|
||||||
QTest::addRow("missing_minute") << "00:"s << std::optional<espchrono::seconds32>{};
|
QTest::addColumn<result_t>("expected");
|
||||||
QTest::addRow("zero") << "00:00"s << std::optional<espchrono::seconds32>{0s};
|
|
||||||
QTest::addRow("zero3") << "00:00:00"s << std::optional<espchrono::seconds32>{0s};
|
QTest::addRow("bullshit") << "bullshit"s << result_t{tl::make_unexpected("invalid daypoint (bullshit)")};
|
||||||
QTest::addRow("random") << "12:34:56"s << std::optional<espchrono::seconds32>{12h+34min+56s};
|
QTest::addRow("missing_minute") << "00:"s << result_t{tl::make_unexpected("invalid daypoint (00:)")};
|
||||||
QTest::addRow("random2") << "12:34"s << std::optional<espchrono::seconds32>{12h+34min};
|
QTest::addRow("zero") << "00:00"s << result_t{0s};
|
||||||
// QTest::addRow("negative") << "-12:34:56"s << std::optional<espchrono::seconds32>{-12h-34min-56s};
|
QTest::addRow("zero3") << "00:00:00"s << result_t{0s};
|
||||||
// QTest::addRow("negative_leading_zero") << "-00:34:56"s << std::optional<espchrono::seconds32>{-34min-56s};
|
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()
|
void test_parseDaypoint()
|
||||||
{
|
{
|
||||||
|
using result_t = tl::expected<espchrono::seconds32, std::string>;
|
||||||
|
|
||||||
QFETCH(std::string, input);
|
QFETCH(std::string, input);
|
||||||
QFETCH(std::optional<espchrono::seconds32>, expected);
|
QFETCH(result_t, expected);
|
||||||
|
|
||||||
FIXEDCOMPARE(espchrono::parseDaypoint(input), expected);
|
FIXEDCOMPARE(espchrono::parseDaypoint(input), expected);
|
||||||
}
|
}
|
||||||
|
@ -53,4 +53,39 @@ equals(CLONE_DATE, 1) {
|
|||||||
|
|
||||||
include($$DATE_DIR/date.pri)
|
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
|
QMAKE_CXXFLAGS += -Wno-missing-field-initializers
|
||||||
|
Reference in New Issue
Block a user