userver: userver/formats/parse/common.hpp Source File
Loading...
Searching...
No Matches
common.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/formats/parse/common.hpp
4/// @brief Parsers and converters for std::chrono::seconds,
5/// std::chrono::system_clock::time_point and integral types
6///
7/// @ingroup userver_universal userver_formats_parse
8
9#include <chrono>
10#include <cstdint>
11#include <limits>
12
13#include <fmt/format.h>
14
15#include <userver/formats/common/meta.hpp>
16#include <userver/formats/parse/to.hpp>
17#include <userver/utils/datetime.hpp>
18#include <userver/utils/datetime/from_string_saturating.hpp>
19#include <userver/utils/meta.hpp>
20#include <userver/utils/string_to_duration.hpp>
21
22USERVER_NAMESPACE_BEGIN
23
24/// Generic parsers and converters
25namespace formats::parse {
26namespace impl {
27
28template <typename T, typename Value>
29void CheckInBounds(const Value& value, T x, T min, T max) {
30 if (x < min || x > max) {
31 throw typename Value::ParseException(
32 fmt::format("Value of '{}' is out of bounds ({} <= {} <= {})", value.GetPath(), min, x, max)
33 );
34 }
35}
36
37template <typename Value>
38float NarrowToFloat(double x, const Value& value) {
39 CheckInBounds<double>(value, x, std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max());
40 return static_cast<float>(x);
41}
42
43template <typename Dst, typename Value, typename Src>
44Dst NarrowToInt(Src x, const Value& value) {
45 static_assert(
46 std::numeric_limits<Src>::min() <= std::numeric_limits<Dst>::min() &&
47 std::numeric_limits<Src>::max() >= std::numeric_limits<Dst>::max(),
48 "expanding cast requested"
49 );
50
51 CheckInBounds<Src>(value, x, std::numeric_limits<Dst>::min(), std::numeric_limits<Dst>::max());
52 return static_cast<Dst>(x);
53}
54
55template <typename Value>
56std::chrono::seconds ToSeconds(const std::string& data, const Value& value) {
57 const auto ms = utils::StringToDuration(data);
58 const auto converted = std::chrono::duration_cast<std::chrono::seconds>(ms);
59 if (converted != ms) {
60 throw typename Value::ParseException(fmt::format(
61 "Value of '{}' = {}ms cannot be represented as "
62 "'std::chrono::seconds' without precision loss",
63 value.GetPath(),
64 ms.count()
65 ));
66 }
67 return converted;
68}
69
70} // namespace impl
71
72template <typename Value>
73float Parse(const Value& value, To<float>) {
74 return impl::NarrowToFloat(value.template As<double>(), value);
75}
76
77template <typename Value, typename T>
78std::enable_if_t<common::kIsFormatValue<Value> && meta::kIsInteger<T>, T> Parse(const Value& value, To<T>) {
79 using IntT = std::conditional_t<std::is_signed<T>::value, int64_t, uint64_t>;
80 return impl::NarrowToInt<T>(value.template As<IntT>(), value);
81}
82
83template <typename Value, typename Period>
84std::enable_if_t<common::kIsFormatValue<Value>, std::chrono::duration<double, Period>>
85Parse(const Value& n, To<std::chrono::duration<double, Period>>) {
86 return std::chrono::duration<double, Period>(n.template As<double>());
87}
88
89template <typename Value>
90std::enable_if_t<common::kIsFormatValue<Value>, std::chrono::seconds> Parse(const Value& n, To<std::chrono::seconds>) {
91 return n.IsInt64() ? std::chrono::seconds{n.template As<int64_t>()}
92 : impl::ToSeconds(n.template As<std::string>(), n);
93}
94
95template <class Value>
96std::chrono::system_clock::time_point Parse(const Value& n, To<std::chrono::system_clock::time_point>) {
97 return utils::datetime::FromRfc3339StringSaturating(n.template As<std::string>());
98}
99
100template <class Value>
101float Convert(const Value& value, To<float>) {
102 return impl::NarrowToFloat(value.template ConvertTo<double>(), value);
103}
104
105template <typename Value, typename T>
106std::enable_if_t<meta::kIsInteger<T>, T> Convert(const Value& value, To<T>) {
107 using IntT = std::conditional_t<std::is_signed<T>::value, int64_t, uint64_t>;
108 return impl::NarrowToInt<T>(value.template ConvertTo<IntT>(), value);
109}
110
111template <typename Value>
112std::chrono::seconds Convert(const Value& n, To<std::chrono::seconds>) {
113 return n.IsInt64() ? std::chrono::seconds{n.template ConvertTo<int64_t>()}
114 : impl::ToSeconds(n.template ConvertTo<std::string>(), n);
115}
116
117} // namespace formats::parse
118
119USERVER_NAMESPACE_END