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 ({} <= {} <= {})",
33 value.GetPath(), min, x, max));
34 }
35}
36
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);
42}
43
44template <typename Dst, typename Value, typename Src>
45Dst NarrowToInt(Src x, const Value& value) {
46 static_assert(
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");
50
51 CheckInBounds<Src>(value, x, std::numeric_limits<Dst>::min(),
52 std::numeric_limits<Dst>::max());
53 return static_cast<Dst>(x);
54}
55
56template <typename Value>
57std::chrono::seconds ToSeconds(const std::string& data, const Value& value) {
58 const auto ms = utils::StringToDuration(data);
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()));
65 }
66 return converted;
67}
68
69} // namespace impl
70
71template <typename Value>
72float Parse(const Value& value, To<float>) {
73 return impl::NarrowToFloat(value.template As<double>(), value);
74}
75
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);
81}
82
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>());
88}
89
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);
95}
96
97template <class Value>
98std::chrono::system_clock::time_point Parse(
99 const Value& n, To<std::chrono::system_clock::time_point>) {
101 n.template As<std::string>());
102}
103
104template <class Value>
105float Convert(const Value& value, To<float>) {
106 return impl::NarrowToFloat(value.template ConvertTo<double>(), value);
107}
108
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);
113}
114
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);
119}
120
121} // namespace formats::parse
122
123USERVER_NAMESPACE_END