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 <cmath>
11#include <cstdint>
12#include <limits>
13
14#include <fmt/format.h>
15
16#include <userver/formats/common/meta.hpp>
17#include <userver/formats/parse/to.hpp>
18#include <userver/utils/datetime/from_string_saturating.hpp>
19#include <userver/utils/datetime_light.hpp>
20#include <userver/utils/meta.hpp>
21#include <userver/utils/string_to_duration.hpp>
22
23USERVER_NAMESPACE_BEGIN
24
25/// Generic parsers and converters
26namespace formats::parse {
27namespace impl {
28
29template <typename T, typename Value>
30void CheckInBounds(const Value& value, T x, T min, T max) {
31 if (x < min || x > max) {
32 throw typename Value::ParseException(
33 fmt::format("Value of '{}' is out of bounds ({} <= {} <= {})", value.GetPath(), min, x, max)
34 );
35 }
36}
37
38template <typename Value>
39void CheckDoubleFitsInFloat(const Value& value, const double dval) {
40 if (!std::isfinite(dval)) {
41 auto msg = fmt::format(
42 "Double value ({}) of '{}' is prohibited for use. Inf and NaN values lead to vulnerabilities in code",
43 dval,
44 value.GetPath()
45 );
46 UASSERT_MSG(false, msg);
47 throw typename Value::ParseException(std::move(msg));
48 }
49
50 auto fval = static_cast<float>(dval);
51
52 if (!std::isfinite(fval)) {
53 throw typename Value::ParseException(
54 fmt::format("Double value ({}) of '{}' does not fit into float", dval, value.GetPath())
55 );
56 }
57}
58
59template <typename Value>
60float NarrowToFloat(double x, const Value& value) {
61 impl::CheckDoubleFitsInFloat(value, x);
62 return static_cast<float>(x);
63}
64
65template <typename Dst, typename Value, typename Src>
66Dst NarrowToInt(Src x, const Value& value) {
67 static_assert(
68 std::numeric_limits<Src>::min() <= std::numeric_limits<Dst>::min() &&
69 std::numeric_limits<Src>::max() >= std::numeric_limits<Dst>::max(),
70 "expanding cast requested"
71 );
72
73 CheckInBounds<Src>(value, x, std::numeric_limits<Dst>::min(), std::numeric_limits<Dst>::max());
74 return static_cast<Dst>(x);
75}
76
77template <typename Value>
78std::chrono::seconds ToSeconds(const std::string& data, const Value& value) {
79 const auto ms = utils::StringToDuration(data);
80 const auto converted = std::chrono::duration_cast<std::chrono::seconds>(ms);
81 if (converted != ms) {
82 throw typename Value::ParseException(fmt::format(
83 "Value of '{}' = {}ms cannot be represented as "
84 "'std::chrono::seconds' without precision loss",
85 value.GetPath(),
86 ms.count()
87 ));
88 }
89 return converted;
90}
91
92} // namespace impl
93
94template <typename Value>
95float Parse(const Value& value, To<float>) {
96 return impl::NarrowToFloat(value.template As<double>(), value);
97}
98
99template <common::kIsFormatValue Value, meta::kIsInteger T>
100T Parse(const Value& value, To<T>) {
101 using IntT = std::conditional_t<std::is_signed<T>::value, int64_t, uint64_t>;
102 return impl::NarrowToInt<T>(value.template As<IntT>(), value);
103}
104
105template <common::kIsFormatValue Value, typename Period>
106std::chrono::duration<double, Period> Parse(const Value& n, To<std::chrono::duration<double, Period>>) {
107 return std::chrono::duration<double, Period>(n.template As<double>());
108}
109
110template <common::kIsFormatValue Value>
111std::chrono::seconds Parse(const Value& n, To<std::chrono::seconds>) {
112 return n.IsInt64() ? std::chrono::seconds{n.template As<int64_t>()}
113 : impl::ToSeconds(n.template As<std::string>(), n);
114}
115
116template <class Value, class Duration>
117std::chrono::time_point<std::chrono::system_clock, Duration>
118Parse(const Value& n, To<std::chrono::time_point<std::chrono::system_clock, Duration>>) {
119 return utils::datetime::FromRfc3339StringSaturating<Duration>(n.template As<std::string>());
120}
121
122template <class Value>
123float Convert(const Value& value, To<float>) {
124 return impl::NarrowToFloat(value.template ConvertTo<double>(), value);
125}
126
127template <typename Value, meta::kIsInteger T>
128T Convert(const Value& value, To<T>) {
129 using IntT = std::conditional_t<std::is_signed<T>::value, int64_t, uint64_t>;
130 return impl::NarrowToInt<T>(value.template ConvertTo<IntT>(), value);
131}
132
133template <typename Value>
134std::chrono::seconds Convert(const Value& n, To<std::chrono::seconds>) {
135 return n.IsInt64() ? std::chrono::seconds{n.template ConvertTo<int64_t>()}
136 : impl::ToSeconds(n.template ConvertTo<std::string>(), n);
137}
138
139} // namespace formats::parse
140
141USERVER_NAMESPACE_END