Astarte device API for C++ 0.8.1
Astarte device SDK for C++
Loading...
Searching...
No Matches
formatter.hpp
Go to the documentation of this file.
1// (C) Copyright 2025, SECO Mind Srl
2//
3// SPDX-License-Identifier: Apache-2.0
4
5#ifndef ASTARTE_FORMATTER_H
6#define ASTARTE_FORMATTER_H
7
16
17#include <chrono>
18#include <cstddef>
19#include <cstdint>
20#include <ctime>
21#include <string>
22#include <string_view>
23#include <type_traits>
24#include <vector>
25
26#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) || \
27 (!defined(_MSVC_LANG) && __cplusplus >= 202002L)) && \
28 (__has_include(<format>))
29
30#include <format>
31namespace astarte_fmt = ::std;
32
33#else
34
35#include <spdlog/fmt/fmt.h>
36
37#include <iomanip>
38#include <sstream>
39namespace astarte_fmt = ::fmt;
40
41#endif
42
43#ifdef DOXYGEN
45namespace astarte_fmt {
47template <typename T>
48struct formatter {};
49} // namespace astarte_fmt
50#endif
51
54
62template <typename OutputIt>
63void format_base64(OutputIt& out, const std::vector<uint8_t>& data) {
64 static constexpr std::string_view base64_chars =
65 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
66 "abcdefghijklmnopqrstuvwxyz"
67 "0123456789+/";
68
69 // Named constants to avoid magic numbers
70 static constexpr uint32_t mask_6bit = 0x3F;
71 static constexpr int shift_byte_0 = 16;
72 static constexpr int shift_byte_1 = 8;
73 static constexpr int shift_b64_0 = 18;
74 static constexpr int shift_b64_1 = 12;
75 static constexpr int shift_b64_2 = 6;
76
77 size_t idx = 0;
78 const size_t len = data.size();
79
80 out = astarte_fmt::format_to(out, "\"");
81
82 while (idx + 2 < len) {
83 const uint32_t chunk =
84 (data[idx] << shift_byte_0) | (data[idx + 1] << shift_byte_1) | data[idx + 2];
85 out = astarte_fmt::format_to(out, "{}{}{}{}", base64_chars[(chunk >> shift_b64_0) & mask_6bit],
86 base64_chars[(chunk >> shift_b64_1) & mask_6bit],
87 base64_chars[(chunk >> shift_b64_2) & mask_6bit],
88 base64_chars[chunk & mask_6bit]);
89 idx += 3;
90 }
91
92 if (idx < len) {
93 uint32_t chunk = data[idx] << shift_byte_0;
94 if (idx + 1 < len) {
95 chunk |= data[idx + 1] << shift_byte_1;
96 }
97
98 out = astarte_fmt::format_to(out, "{}{}", base64_chars[(chunk >> shift_b64_0) & mask_6bit],
99 base64_chars[(chunk >> shift_b64_1) & mask_6bit]);
100 if (idx + 1 < len) {
101 out = astarte_fmt::format_to(out, "{}=", base64_chars[(chunk >> shift_b64_2) & mask_6bit]);
102 } else {
103 out = astarte_fmt::format_to(out, "==");
104 }
105 }
106
107 out = astarte_fmt::format_to(out, "\"");
108}
109
117template <typename OutputIt>
118void format_timestamp(OutputIt& out, const std::chrono::system_clock::time_point& data) {
119 out = astarte_fmt::format_to(out, "\"");
120#if (__cplusplus >= 202002L) && (__has_include(<format>))
121 out = astarte_fmt::format_to(
122 out, "{}",
123 astarte_fmt::format("{0:%F}T{0:%T}Z",
124 std::chrono::time_point_cast<std::chrono::milliseconds>(data)));
125#else // (__cplusplus >= 202002L) && (__has_include(<format>))
126 const std::time_t time = std::chrono::system_clock::to_time_t(data);
127 const std::tm utc_tm = *std::gmtime(&time);
128 std::stringstream stream;
129 stream << std::put_time(&utc_tm, "%FT%T.000Z");
130 out = astarte_fmt::format_to(out, "{}", stream.str());
131#endif // (__cplusplus >= 202002L) && (__has_include(<format>))
132 out = astarte_fmt::format_to(out, "\"");
133}
134
143template <typename OutputIt, typename T>
144void format_data(OutputIt& out, const T& data) {
145 if constexpr (std::is_same_v<T, bool>) {
146 astarte_fmt::format_to(out, "{}", (data ? "true" : "false"));
147 } else if constexpr (std::is_same_v<T, std::string>) {
148 astarte_fmt::format_to(out, R"("{}")", data);
149 } else if constexpr (std::is_same_v<T, std::vector<uint8_t>>) {
150 format_base64(out, data);
151 } else if constexpr (std::is_same_v<T, std::chrono::system_clock::time_point>) {
152 format_timestamp(out, data);
153 } else { // default format case
154 astarte_fmt::format_to(out, "{}", data);
155 }
156}
157
166template <typename OutputIt, typename T>
167void format_vector(OutputIt& out, const std::vector<T>& data) {
168 out = astarte_fmt::format_to(out, "[");
169 for (size_t i = 0; i < data.size(); ++i) {
170 format_data(out, data[i]);
171 if (i != data.size() - 1) {
172 out = astarte_fmt::format_to(out, ", ");
173 }
174 }
175 out = astarte_fmt::format_to(out, "]");
176}
177
178} // namespace astarte::device::utils
179
180#endif // ASTARTE_FORMATTER_H
Utility functions for formatting data.
Definition formatter.hpp:53
void format_vector(OutputIt &out, const std::vector< T > &data)
Format a generic vector into a comma-separated list in brackets.
Definition formatter.hpp:167
void format_timestamp(OutputIt &out, const std::chrono::system_clock::time_point &data)
Format a timestamp into an ISO 8601 string literal.
Definition formatter.hpp:118
void format_data(OutputIt &out, const T &data)
Format a generic data type into an output iterator.
Definition formatter.hpp:144
void format_base64(OutputIt &out, const std::vector< uint8_t > &data)
Format a vector of bytes into a Base64 string literal.
Definition formatter.hpp:63
Namespace alias for the formatting library (std or fmt).
Definition formatter.hpp:45
Base formatter struct declaration for Doxygen.
Definition formatter.hpp:48