13#include <fmt/format.h>
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>
22USERVER_NAMESPACE_BEGIN
25namespace formats::
parse {
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 ({} <= {} <= {})",
33 value.GetPath(), min, x, max));
37template <
typename Value>
38float NarrowToFloat(
double x,
const Value& value) {
39 CheckInBounds<
double>(value, x, std::numeric_limits<
float>::lowest(),
40 std::numeric_limits<
float>::max());
41 return static_cast<
float>(x);
44template <
typename Dst,
typename Value,
typename Src>
45Dst NarrowToInt(Src x,
const Value& value) {
47 std::numeric_limits<Src>::min() <= std::numeric_limits<Dst>::min() &&
48 std::numeric_limits<Src>::max() >= std::numeric_limits<Dst>::max(),
49 "expanding cast requested");
51 CheckInBounds<Src>(value, x, std::numeric_limits<Dst>::min(),
52 std::numeric_limits<Dst>::max());
53 return static_cast<Dst>(x);
56template <
typename Value>
57std::chrono::seconds ToSeconds(
const std::string& data,
const Value& value) {
59 const auto converted = std::chrono::duration_cast<std::chrono::seconds>(ms);
60 if (converted != ms) {
61 throw typename Value::ParseException(
62 fmt::format(
"Value of '{}' = {}ms cannot be represented as "
63 "'std::chrono::seconds' without precision loss",
64 value.GetPath(), ms.count()));
71template <
typename Value>
72float Parse(
const Value& value,
To<
float>) {
73 return impl::NarrowToFloat(value.
template As<
double>(), value);
76template <
typename Value,
typename T>
77std::enable_if_t<common::kIsFormatValue<Value> && meta::kIsInteger<T>, T> Parse(
78 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);
83template <
typename Value,
typename Period>
84std::enable_if_t<common::kIsFormatValue<Value>,
85 std::chrono::duration<
double, Period>>
86Parse(
const Value& n, To<std::chrono::duration<
double, Period>>) {
87 return std::chrono::duration<
double, Period>(n.
template As<
double>());
90template <
typename Value>
91std::enable_if_t<common::kIsFormatValue<Value>, std::chrono::seconds> Parse(
92 const Value& n, To<std::chrono::seconds>) {
93 return n.IsInt64() ? std::chrono::seconds{n.
template As<int64_t>()}
94 : impl::ToSeconds(n.
template As<std::string>(), n);
98std::chrono::system_clock::time_point Parse(
99 const Value& n,
To<std::chrono::system_clock::time_point>) {
101 n.
template As<std::string>()
);
104template <
class Value>
105float Convert(
const Value& value,
To<
float>) {
106 return impl::NarrowToFloat(value.
template ConvertTo<
double>(), value);
109template <
typename Value,
typename T>
110std::enable_if_t<meta::kIsInteger<T>, T> Convert(
const Value& value, To<T>) {
111 using IntT = std::conditional_t<std::is_signed<T>::value, int64_t, uint64_t>;
112 return impl::NarrowToInt<T>(value.
template ConvertTo<IntT>(), value);
115template <
typename Value>
116std::chrono::seconds Convert(
const Value& n, To<std::chrono::seconds>) {
117 return n.IsInt64() ? std::chrono::seconds{n.
template ConvertTo<int64_t>()}
118 : impl::ToSeconds(n.
template ConvertTo<std::string>(), n);