userver: userver/formats/parse/common.hpp Source File
⚠️ This is the documentation for an old userver version. Click here to switch to the latest version.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
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