14#include <fmt/format.h>
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>
23USERVER_NAMESPACE_BEGIN
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)
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",
47 throw typename Value::ParseException(std::move(msg));
50 auto fval =
static_cast<
float>(dval);
52 if (!std::isfinite(fval)) {
53 throw typename Value::ParseException(
54 fmt::format(
"Double value ({}) of '{}' does not fit into float", dval, value.GetPath())
59template <
typename Value>
60float NarrowToFloat(
double x,
const Value& value) {
61 impl::CheckDoubleFitsInFloat(value, x);
62 return static_cast<
float>(x);
65template <
typename Dst,
typename Value,
typename Src>
66Dst NarrowToInt(Src x,
const Value& value) {
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"
73 CheckInBounds<Src>(value, x, std::numeric_limits<Dst>::min(), std::numeric_limits<Dst>::max());
74 return static_cast<Dst>(x);
77template <
typename Value>
78std::chrono::seconds ToSeconds(
const std::string& data,
const Value& value) {
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",
94template <
typename Value>
95float Parse(
const Value& value,
To<
float>) {
96 return impl::NarrowToFloat(value.
template As<
double>(), value);
99template <
typename Value,
typename T>
100std::enable_if_t<
common::kIsFormatValue<Value> && meta::kIsInteger<T>, T> 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);
105template <
typename Value,
typename Period>
106std::enable_if_t<
common::kIsFormatValue<Value>, std::chrono::duration<
double, Period>>
107Parse(
const Value& n,
To<std::chrono::duration<
double, Period>>) {
108 return std::chrono::duration<
double, Period>(n.
template As<
double>());
111template <
typename Value>
112std::enable_if_t<
common::kIsFormatValue<Value>, std::chrono::seconds> Parse(
const Value& n,
To<std::chrono::seconds>) {
113 return n.IsInt64() ? std::chrono::seconds{n.
template As<int64_t>()}
114 : impl::ToSeconds(n.
template As<std::string>(), n);
117template <
class Value,
class Duration>
118std::chrono::time_point<std::chrono::system_clock, Duration>
119Parse(
const Value& n,
To<std::chrono::time_point<std::chrono::system_clock, Duration>>) {
120 return utils::
datetime::FromRfc3339StringSaturating<Duration>(n.
template As<std::string>());
123template <
class Value>
124float Convert(
const Value& value,
To<
float>) {
125 return impl::NarrowToFloat(value.
template ConvertTo<
double>(), value);
128template <
typename Value,
typename T>
129std::enable_if_t<meta::kIsInteger<T>, T> Convert(
const Value& value,
To<T>) {
130 using IntT = std::conditional_t<std::is_signed<T>::value, int64_t, uint64_t>;
131 return impl::NarrowToInt<T>(value.
template ConvertTo<IntT>(), value);
134template <
typename Value>
135std::chrono::seconds Convert(
const Value& n,
To<std::chrono::seconds>) {
136 return n.IsInt64() ? std::chrono::seconds{n.
template ConvertTo<int64_t>()}
137 : impl::ToSeconds(n.
template ConvertTo<std::string>(), n);