sparrow 0.3.0
Loading...
Searching...
No Matches
format.hpp
Go to the documentation of this file.
1// Copyright 2024 Man Group Operations Limited
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or mplied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15
16#pragma once
17
18#include <algorithm>
19#include <cstddef>
20#include <format>
21#include <numeric>
22#include <ranges>
23#include <string>
24#include <variant>
25#include <vector>
26
28
29namespace std
30{
31 template <class... T>
32 struct formatter<std::variant<T...>>
33 {
34 constexpr auto parse(format_parse_context& ctx)
35 {
36 auto pos = ctx.begin();
37 while (pos != ctx.end() && *pos != '}')
38 {
39 m_format_string.push_back(*pos);
40 ++pos;
41 }
42 m_format_string.push_back('}');
43 return pos;
44 }
45
46 auto format(const std::variant<T...>& v, std::format_context& ctx) const
47 {
48 return std::visit(
49 [&ctx, this](const auto& value)
50 {
51 return std::vformat_to(ctx.out(), m_format_string, std::make_format_args(value));
52 },
53 v
54 );
55 }
56
57 std::string m_format_string = "{:";
58 };
59}
60
61namespace sparrow
62{
63 template <typename R>
64 concept RangeOfRanges = std::ranges::range<R> && std::ranges::range<std::ranges::range_value_t<R>>;
65
66 template <typename T>
67 concept Format = requires(const T& t) { std::format(t, 1); };
68
69 template <typename T>
70 concept RangeOfFormats = std::ranges::range<T> && Format<std::ranges::range_value_t<T>>;
71
72 constexpr size_t max_width(const std::ranges::input_range auto& data)
73 {
74 size_t max_width = 0;
75 for (const auto& value : data)
76 {
77 max_width = std::max(max_width, std::format("{}", value).size());
78 }
79 return max_width;
80 }
81
82 template <RangeOfRanges Columns>
83 constexpr std::vector<size_t> columns_widths(const Columns& columns)
84 {
85 std::vector<size_t> widths;
86 widths.reserve(std::ranges::size(columns));
87 for (const auto& col : columns)
88 {
89 widths.push_back(max_width(col));
90 }
91 return widths;
92 }
93
94 template <typename OutputIt, std::ranges::input_range Widths, std::ranges::input_range Values>
95 requires(std::same_as<std::ranges::range_value_t<Widths>, size_t>)
96 constexpr void
97 to_row(OutputIt out, const Widths& widths, const Values& values, std::string_view separator = "|")
98 {
99 SPARROW_ASSERT_TRUE(std::ranges::size(widths) == std::ranges::size(values))
100 if (std::ranges::size(values) == 0)
101 {
102 return;
103 }
104 auto value_it = values.begin();
105 auto width_it = widths.begin();
106 for (size_t i = 0; i < std::ranges::size(values); ++i)
107 {
108 const std::string fmt = std::format("{}{{:>{}}}", separator, *width_it);
109 const auto& value = *value_it;
110 std::vformat_to(out, fmt, std::make_format_args(value));
111 ++value_it;
112 ++width_it;
113 }
114 std::format_to(out, "{}", separator);
115 }
116
117 template <typename OutputIt>
118 constexpr void
119 horizontal_separator(OutputIt out, const std::vector<size_t>& widths, std::string_view separator = "-")
120 {
121 if (std::ranges::size(widths) == 0)
122 {
123 return;
124 }
125 const size_t count = std::ranges::size(widths) + 1 + std::reduce(widths.begin(), widths.end());
126 std::format_to(out, "{}", std::string(count, separator[0]));
127 }
128
129#if defined(__clang__)
130# pragma clang diagnostic push
131# pragma clang diagnostic ignored "-Wsign-conversion"
132#endif
133#if defined(__GNUC__)
134# pragma GCC diagnostic push
135# pragma GCC diagnostic ignored "-Wsign-conversion"
136#endif
137 template <std::ranges::input_range Headers, RangeOfRanges Columns, typename OutputIt>
138 requires(std::convertible_to<std::ranges::range_value_t<Headers>, std::string>)
139 constexpr void to_table_with_columns(OutputIt out, const Headers& headers, const Columns& columns)
140 {
141 const size_t column_count = std::ranges::size(columns);
142 SPARROW_ASSERT_TRUE(std::ranges::size(headers) == column_count);
143 if (column_count == 0)
144 {
145 return;
146 }
147
148 // columns lenght must be the same
149 if (column_count > 0)
150 {
151 for (auto it = columns.begin() + 1; it != columns.end(); ++it)
152 {
153 SPARROW_ASSERT_TRUE(std::ranges::size(columns[0]) == std::ranges::size(*it));
154 }
155 }
156
157 std::vector<size_t> widths = columns_widths(columns);
158
159 // max with names
160 for (size_t i = 0; i < std::ranges::size(headers); ++i)
161 {
162 widths[i] = std::max(widths[i], std::ranges::size(headers[i]));
163 }
164 to_row(out, widths, headers);
165 std::format_to(out, "{}", '\n');
166 horizontal_separator(out, widths);
167 std::format_to(out, "{}", '\n');
168
169 // print data
170 for (size_t i = 0; i < std::ranges::size(columns[0]); ++i)
171 {
172 const auto row_range = std::views::transform(
173 columns,
174 [i](const auto& column)
175 {
176 return column[i];
177 }
178 );
179 to_row(out, widths, row_range);
180 std::format_to(out, "{}", '\n');
181 }
182
183 horizontal_separator(out, widths);
184 }
185#if defined(__GNUC__)
186# pragma GCC diagnostic pop
187#endif
188#if defined(__clang__)
189# pragma clang diagnostic pop
190#endif
191}
#define SPARROW_ASSERT_TRUE(expr__)
constexpr void horizontal_separator(OutputIt out, const std::vector< size_t > &widths, std::string_view separator="-")
Definition format.hpp:119
constexpr void to_row(OutputIt out, const Widths &widths, const Values &values, std::string_view separator="|")
Definition format.hpp:97
constexpr void to_table_with_columns(OutputIt out, const Headers &headers, const Columns &columns)
Definition format.hpp:139
constexpr size_t max_width(const std::ranges::input_range auto &data)
Definition format.hpp:72
constexpr std::vector< size_t > columns_widths(const Columns &columns)
Definition format.hpp:83
constexpr auto parse(format_parse_context &ctx)
Definition format.hpp:34
auto format(const std::variant< T... > &v, std::format_context &ctx) const
Definition format.hpp:46