-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfstring.hpp
197 lines (159 loc) · 6.03 KB
/
fstring.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
/**
* @file fstring.hpp
* @brief A quick C++ formatting library
* @version 0.1
*
* Copyright (c) 2022 Maysara Elshewehy (xeerx.com) ([email protected])
*
* Distributed under the MIT License (MIT)
*/
#pragma once
#include <cstddef> // std::size_t
#include <string> // std::string
#include <stdexcept> // std::runtime_error
#include <ctype.h> // isdigit
#include <utility>
#include <vector> // std::vector
#include <algorithm> // std::find
// to save padding begin,end positions and value
struct padd_vec
{
std::size_t begin, end, value;
};
class fstring
{
private:
std::string src;
std::size_t lpos = 0; // last postion for variables() function
std::vector<padd_vec> poss; // to remember positions before formatting in padding()
protected:
// find paddings in string and handle it
void padding() noexcept(false)
{
// handle
for (std::size_t pos = 0; pos < src.length(); pos++)
{
// find '%'
if((pos = src.find('%', pos)) == src.npos) break;
// now we got the position of first "%", so increase position to get next character
auto tmp = pos ++; // save begin position
// what if the character "%" is last character in the string ?
if(pos >= src.length()) break;
// find '.' : for smart padding (padding - length of section)
bool smart_padding = src[pos] == '.';
// increase position to get the next character after "%."
if(smart_padding) pos ++;
// find padding
uint32_t padd = 0; // to store padding value
uint8_t digits = 0; // to store digits count
while(pos < src.length() && isdigit(src[pos]))
{
// get the number
auto n = src[pos] - '0';
// if the first number is zero
if(!n && !padd) throw std::runtime_error("Padding can not be zero OR start with zero");
// calc padding
padd *= 10; padd += n;
// increase position to get the next character
pos ++;
// increase count of digits
digits ++;
}
// if padding is zero so there is nothing to do so skip it !
if(!padd) continue;
// smart padding: save positions to apply after putting the variables value
if(smart_padding)
{
std::size_t end;
// find ".%" : the end of section
if ((end = src.find(".%", pos)) == src.npos) throw std::runtime_error("smart padding must start by %. and ended by .%");
poss.push_back(padd_vec{tmp, end - 4, padd});
src.erase(end, 2); // erase ".%"
src.erase(tmp, digits + 2); // erase "%.n"
// update position to smart padding
pos -= (digits + 2);
}
// normal padding: apply
else
{
// erase %n
src.erase(tmp, digits + 1);
// add spaces(padding)
src.insert(tmp, padd, ' ');
// update position to avoid searching in spaces area
pos += padd - (digits + 1);
}
}
}
// apply smart padding
void apply()
{
for (auto i = poss.begin(); i < poss.end(); i++)
{
// get length of section
std::size_t length = i->end - i->begin;
// if length < padding value so there are a space to add padding
if(i->value > length)
{
// calc padding value: padding - length = the rest of space
std::size_t padd = i->value - length;
// add spaces(padding)
src.insert(i->end, padd, ' ');
// now we updated the string value, so the length is updated
// so we need to update the upcoming positions with: +=padd
for (auto t = i; t < poss.end(); t++) { t->begin += padd; t->end += padd; }
}
}
}
// inline helpers for variables()
inline std::string tostr(std::string ref) { return ref; }
inline std::string tostr(const char * ref) { return std::string(ref); }
template<typename Arg>
inline std::string tostr(Arg ref) { return std::to_string(ref); }
// replace "{}" by values
template<typename T>
void variables(T&& arg)
{
// to avoid error(out of range exception)
if(lpos == src.npos) return;
// find "{}"
if ((lpos = src.find("{}", lpos)) == src.npos) return;
// store arg value as string
auto val = tostr(std::forward<T>(arg));
auto val_len = val.length();
// save
src.erase(lpos, 2); // erase "{}"
src.insert(lpos, val); // add arg value in position of "{}"
// now we updated the string value, so the length is updated
// so we need to update the upcoming positions with: +=val.length()
for (auto i = poss.begin(); i < poss.end(); i++)
{
// skip values smaller than the lpos
if(i->end <= lpos) continue;
// increase
i->end += val_len;
// we cleared the "{}" characters so we need to go back 2 characters if possible
if(i->end > 2) i->end -=2;
// begin of non-first
if(i != poss.begin())
{
i->begin += val_len;
if(i->begin > 2) i->begin -=2;
}
}
}
public:
// constructor
template<typename... T>
fstring(std::string _src, T&& ...args) : src{std::move(_src)}
{
// performance improvement, see: https://cplusplus.com/reference/vector/vector/reserve/
poss.reserve( 5);
src .reserve(250);
padding();
( variables(std::forward<T>(args)) , ...);
apply ();
}
// get result
std::string& get(){ return src; }
};