Files
fennec/include/fennec/format/format.h
Medusa Slockbower 6d58105734 - Switched back to custom window management, taking another stab
- Refactored lang yet again, `fennec/lang` is now C++ language. `fennec/string` `fennec/filesystem` and `fennec/format` are now independent.
2025-12-04 01:04:36 -05:00

141 lines
3.4 KiB
C++

// =====================================================================================================================
// fennec, a free and open source game engine
// Copyright © 2025 Medusa Slockbower
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// =====================================================================================================================
///
/// \file format.h
/// \brief
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///
#ifndef FENNEC_LANG_STRINGS_FORMAT_H
#define FENNEC_LANG_STRINGS_FORMAT_H
#include <fennec/string/string.h>
#include <fennec/format/detail/_format.h>
#include <fennec/format/formatter.h>
namespace fennec
{
template<typename...ArgsT>
string format(const cstring& str, ArgsT&&...args) {
static constexpr size_t argc = sizeof...(ArgsT);
static constexpr format_arg default_fmt = {
.fill { // default no fill
.c = '\0',
.n = 0,
},
.align = '\0', // default to locale
.sign = '-', // default to sign only for negative numbers
.alt = false, // default no prefix
.width = 0,
.precision = 0,
.type = '\0',
};
// empty case
if constexpr(argc == 0) {
return str;
}
detail::_format_argarray<argc> argarray = { fennec::forward<ArgsT>(args)... };
string res;
size_t i = 0;
size_t a = -1;
while (i <= str.length()) {
size_t b = str.find('{', i);
size_t e = str.find('}', i);
format_arg fmt = default_fmt;
// check for '}}'
if (e < b) {
if (str[e + 1] == '}') {
res += string(str.data() + i, e - i);
i = e + 2;
continue;
}
assertf(false, "fennec::format syntax error, encountered unexpected '{'")
}
// append string
if (b >= str.length()) { // handle end case
res += string(str.data() + i, str.length() - i);
break;
}
res += string(str.data() + i, b - i);
// next brace, validate escape
size_t nb = str.find('{', b + 1);
if (b + 1 == nb) {
res += '{';
i = nb + 1;
continue;
}
// find contained colon
size_t c = str.find(':', b);
// validate colon and brace location
assertf(c < nb or e < nb, "fennec::format syntax error, mismatched '{}'");
// parse index if present
size_t id = min(c, e) - 1;
if (id > b) {
a = 0;
} else {
++a;
}
for (size_t j = id, k = 1; j > b; --j, k *= 10) {
size_t u = (str[j] - '0');
assertf(u < 10, "fennec::format syntax error, invalid argument index");
a += k * u;
}
// validate index
assertf(a < argc, "fennec::format syntax error, invalid argument index");
// early return case for no colon
if (c > e) {
res += argarray.format(a, fmt);
i = e + 1;
continue;
}
// parse format specifiers
// add formatted argument
res += argarray.format(a, fmt);
i = e + 1;
}
return res;
}
}
#endif // FENNEC_LANG_STRINGS_FORMAT_H