mirror of
https://github.com/matrix-construct/construct
synced 2024-11-30 02:32:43 +01:00
918 lines
19 KiB
C++
918 lines
19 KiB
C++
// Matrix Construct
|
|
//
|
|
// Copyright (C) Matrix Construct Developers, Authors & Contributors
|
|
// Copyright (C) 2016-2018 Jason Volk <jason@zemos.net>
|
|
//
|
|
// Permission to use, copy, modify, and/or distribute this software for any
|
|
// purpose with or without fee is hereby granted, provided that the above
|
|
// copyright notice and this permission notice is present in all copies. The
|
|
// full license for this software is available in the LICENSE file.
|
|
|
|
#include <ircd/spirit.h>
|
|
|
|
namespace ircd::fmt
|
|
{
|
|
using namespace ircd::spirit;
|
|
|
|
struct spec;
|
|
struct specifier;
|
|
|
|
constexpr char SPECIFIER
|
|
{
|
|
'%'
|
|
};
|
|
|
|
constexpr char SPECIFIER_TERMINATOR
|
|
{
|
|
'$'
|
|
};
|
|
|
|
struct parser extern const parser;
|
|
extern std::map<string_view, specifier *, std::less<>> specifiers;
|
|
|
|
bool is_specifier(const string_view &name);
|
|
void handle_specifier(char *&out, const size_t &max, const uint &idx, const spec &, const arg &);
|
|
template<class generator> bool generate_string(char *&out, const generator &gen, const arg &val);
|
|
template<class T, class lambda> bool visit_type(const arg &val, lambda&& closure);
|
|
}
|
|
|
|
// Structural representation of a format specifier
|
|
struct ircd::fmt::spec
|
|
{
|
|
char sign {'+'};
|
|
ushort width {0};
|
|
string_view name;
|
|
|
|
spec() = default;
|
|
};
|
|
|
|
BOOST_FUSION_ADAPT_STRUCT
|
|
(
|
|
ircd::fmt::spec,
|
|
( decltype(ircd::fmt::spec::sign), sign )
|
|
( decltype(ircd::fmt::spec::width), width )
|
|
( decltype(ircd::fmt::spec::name), name )
|
|
)
|
|
|
|
// A format specifier handler module.
|
|
// This allows a new "%foo" to be defined with custom handling.
|
|
class ircd::fmt::specifier
|
|
{
|
|
std::set<std::string> names;
|
|
|
|
public:
|
|
virtual bool operator()(char *&out, const size_t &max, const spec &, const arg &) const = 0;
|
|
|
|
specifier(const std::initializer_list<std::string> &names);
|
|
specifier(const std::string &name);
|
|
virtual ~specifier() noexcept;
|
|
};
|
|
|
|
decltype(ircd::fmt::specifiers)
|
|
ircd::fmt::specifiers;
|
|
|
|
struct ircd::fmt::parser
|
|
:qi::grammar<const char *, fmt::spec>
|
|
{
|
|
template<class R = unused_type> using rule = qi::rule<const char *, R>;
|
|
|
|
const rule<> specsym
|
|
{
|
|
lit(SPECIFIER)
|
|
,"format specifier"
|
|
};
|
|
|
|
const rule<> specterm
|
|
{
|
|
lit(SPECIFIER_TERMINATOR)
|
|
,"specifier termination"
|
|
};
|
|
|
|
const rule<string_view> name
|
|
{
|
|
raw[repeat(1,14)[char_("A-Za-z")]]
|
|
,"specifier name"
|
|
};
|
|
|
|
rule<fmt::spec> spec;
|
|
|
|
parser()
|
|
:parser::base_type{spec}
|
|
{
|
|
static const auto is_valid([]
|
|
(const auto &str, auto &, auto &valid)
|
|
{
|
|
valid = is_specifier(str);
|
|
});
|
|
|
|
spec %= specsym >> -(char_('+') | char_('-')) >> -ushort_ >> name[is_valid] >> -specterm;
|
|
}
|
|
}
|
|
const ircd::fmt::parser;
|
|
|
|
namespace ircd {
|
|
namespace fmt {
|
|
|
|
struct string_specifier
|
|
:specifier
|
|
{
|
|
static const std::tuple
|
|
<
|
|
const char *,
|
|
std::string,
|
|
std::string_view,
|
|
ircd::string_view,
|
|
ircd::json::string,
|
|
ircd::json::object,
|
|
ircd::json::array
|
|
>
|
|
types;
|
|
|
|
bool operator()(char *&out, const size_t &max, const spec &, const arg &val) const override;
|
|
using specifier::specifier;
|
|
}
|
|
const string_specifier
|
|
{
|
|
"s"s
|
|
};
|
|
|
|
decltype(string_specifier::types)
|
|
string_specifier::types
|
|
{};
|
|
|
|
struct bool_specifier
|
|
:specifier
|
|
{
|
|
static const std::tuple
|
|
<
|
|
bool,
|
|
char, unsigned char,
|
|
short, unsigned short,
|
|
int, unsigned int,
|
|
long, unsigned long,
|
|
long long, unsigned long long
|
|
>
|
|
types;
|
|
|
|
bool operator()(char *&out, const size_t &max, const spec &, const arg &val) const override;
|
|
using specifier::specifier;
|
|
}
|
|
const bool_specifier
|
|
{
|
|
{ "b"s }
|
|
};
|
|
|
|
decltype(bool_specifier::types)
|
|
bool_specifier::types
|
|
{};
|
|
|
|
struct signed_specifier
|
|
:specifier
|
|
{
|
|
static const std::tuple
|
|
<
|
|
bool,
|
|
char, unsigned char,
|
|
short, unsigned short,
|
|
int, unsigned int,
|
|
long, unsigned long,
|
|
long long, unsigned long long
|
|
>
|
|
types;
|
|
|
|
bool operator()(char *&out, const size_t &max, const spec &, const arg &val) const override;
|
|
using specifier::specifier;
|
|
}
|
|
const signed_specifier
|
|
{
|
|
{ "d"s, "ld"s, "zd"s }
|
|
};
|
|
|
|
decltype(signed_specifier::types)
|
|
signed_specifier::types
|
|
{};
|
|
|
|
struct unsigned_specifier
|
|
:specifier
|
|
{
|
|
static const std::tuple
|
|
<
|
|
bool,
|
|
char, unsigned char,
|
|
short, unsigned short,
|
|
int, unsigned int,
|
|
long, unsigned long,
|
|
long long, unsigned long long
|
|
>
|
|
types;
|
|
|
|
bool operator()(char *&out, const size_t &max, const spec &, const arg &val) const override;
|
|
using specifier::specifier;
|
|
}
|
|
const unsigned_specifier
|
|
{
|
|
{ "u"s, "lu"s, "zu"s }
|
|
};
|
|
|
|
struct hex_lowercase_specifier
|
|
:specifier
|
|
{
|
|
static const std::tuple
|
|
<
|
|
bool,
|
|
char, unsigned char,
|
|
short, unsigned short,
|
|
int, unsigned int,
|
|
long, unsigned long,
|
|
long long, unsigned long long
|
|
>
|
|
types;
|
|
|
|
bool operator()(char *&out, const size_t &max, const spec &, const arg &val) const override;
|
|
using specifier::specifier;
|
|
}
|
|
const hex_lowercase_specifier
|
|
{
|
|
{ "x"s, "lx"s }
|
|
};
|
|
|
|
decltype(hex_lowercase_specifier::types)
|
|
hex_lowercase_specifier::types
|
|
{};
|
|
|
|
decltype(unsigned_specifier::types)
|
|
unsigned_specifier::types
|
|
{};
|
|
|
|
struct float_specifier
|
|
:specifier
|
|
{
|
|
static const std::tuple
|
|
<
|
|
char, unsigned char,
|
|
short, unsigned short,
|
|
int, unsigned int,
|
|
long, unsigned long,
|
|
float, double
|
|
>
|
|
types;
|
|
|
|
bool operator()(char *&out, const size_t &max, const spec &, const arg &val) const override;
|
|
using specifier::specifier;
|
|
}
|
|
const float_specifier
|
|
{
|
|
{ "f"s, "lf"s }
|
|
};
|
|
|
|
decltype(float_specifier::types)
|
|
float_specifier::types
|
|
{};
|
|
|
|
struct char_specifier
|
|
:specifier
|
|
{
|
|
bool operator()(char *&out, const size_t &max, const spec &, const arg &val) const override;
|
|
using specifier::specifier;
|
|
}
|
|
const char_specifier
|
|
{
|
|
"c"s
|
|
};
|
|
|
|
struct pointer_specifier
|
|
:specifier
|
|
{
|
|
bool operator()(char *&out, const size_t &max, const spec &, const arg &val) const override;
|
|
using specifier::specifier;
|
|
}
|
|
const pointer_specifier
|
|
{
|
|
"p"s
|
|
};
|
|
|
|
} // namespace fmt
|
|
} // namespace ircd
|
|
|
|
using namespace ircd;
|
|
|
|
fmt::snprintf::snprintf(internal_t,
|
|
char *const &out,
|
|
const size_t &max,
|
|
const char *const &fstr,
|
|
const va_rtti &v)
|
|
try
|
|
:fstart{strchr(fstr, SPECIFIER)}
|
|
,fstop{fstr}
|
|
,fend{fstr + strnlen(fstr, max)}
|
|
,obeg{out}
|
|
,oend{out + max}
|
|
,out{out}
|
|
,idx{0}
|
|
{
|
|
if(unlikely(!max))
|
|
{
|
|
fstart = nullptr;
|
|
return;
|
|
}
|
|
|
|
if(!fstart)
|
|
{
|
|
const mutable_buffer dst{out, max};
|
|
const const_buffer src{fstr, fend};
|
|
this->out += size_t(strlcpy(dst, src));
|
|
return;
|
|
}
|
|
|
|
append(fstr, fstart);
|
|
auto it(begin(v));
|
|
for(size_t i(0); i < v.size() && !finished(); ++it, i++)
|
|
{
|
|
const auto &ptr(get<0>(*it));
|
|
const auto &type(get<1>(*it));
|
|
argument(std::make_tuple(ptr, std::type_index(*type)));
|
|
}
|
|
|
|
assert(this->out >= obeg);
|
|
assert(this->out < oend);
|
|
*this->out = '\0';
|
|
}
|
|
catch(const std::out_of_range &e)
|
|
{
|
|
throw invalid_format
|
|
{
|
|
"Format string requires more than %zu arguments.", v.size()
|
|
};
|
|
}
|
|
|
|
void
|
|
fmt::snprintf::argument(const arg &val)
|
|
{
|
|
fmt::spec spec;
|
|
if(qi::parse(fstart, fend, parser, spec))
|
|
handle_specifier(out, remaining(), idx++, spec, val);
|
|
|
|
fstop = fstart;
|
|
if(fstart >= fend)
|
|
return;
|
|
|
|
fstart = strchr(fstart, SPECIFIER);
|
|
append(fstop, fstart?: fend);
|
|
}
|
|
|
|
void
|
|
fmt::snprintf::append(const char *const &begin,
|
|
const char *const &end)
|
|
{
|
|
assert(begin <= end);
|
|
const size_t rem(remaining());
|
|
const size_t len(end - begin);
|
|
const size_t &cpsz
|
|
{
|
|
std::min(len, rem)
|
|
};
|
|
|
|
memcpy(out, begin, cpsz);
|
|
out += cpsz;
|
|
assert(out < oend);
|
|
}
|
|
|
|
size_t
|
|
fmt::snprintf::remaining()
|
|
const
|
|
{
|
|
assert(out < oend);
|
|
assert(obeg <= oend);
|
|
const ssize_t r(oend - out);
|
|
return std::max(r - 1, ssize_t(0));
|
|
}
|
|
|
|
bool
|
|
fmt::snprintf::finished()
|
|
const
|
|
{
|
|
return !fstart || fstop >= fend || remaining() == 0;
|
|
}
|
|
|
|
fmt::specifier::specifier(const std::string &name)
|
|
:specifier{{name}}
|
|
{
|
|
}
|
|
|
|
fmt::specifier::specifier(const std::initializer_list<std::string> &names)
|
|
:names{names}
|
|
{
|
|
for(const auto &name : this->names)
|
|
if(is_specifier(name))
|
|
throw error
|
|
{
|
|
"Specifier '%s' already registered\n", name
|
|
};
|
|
|
|
for(const auto &name : this->names)
|
|
specifiers.emplace(name, this);
|
|
}
|
|
|
|
fmt::specifier::~specifier()
|
|
noexcept
|
|
{
|
|
for(const auto &name : names)
|
|
specifiers.erase(name);
|
|
}
|
|
|
|
bool
|
|
fmt::is_specifier(const string_view &name)
|
|
{
|
|
return specifiers.count(name);
|
|
}
|
|
|
|
void
|
|
fmt::handle_specifier(char *&out,
|
|
const size_t &max,
|
|
const uint &idx,
|
|
const spec &spec,
|
|
const arg &val)
|
|
try
|
|
{
|
|
const auto &type(get<1>(val));
|
|
const auto &handler(*specifiers.at(spec.name));
|
|
if(unlikely(!handler(out, max, spec, val)))
|
|
throw invalid_type
|
|
{
|
|
"`%s' (%s) for format specifier '%s' for argument #%u",
|
|
demangle(type.name()),
|
|
type.name(),
|
|
spec.name,
|
|
idx
|
|
};
|
|
}
|
|
catch(const std::out_of_range &e)
|
|
{
|
|
throw invalid_format
|
|
{
|
|
"Unhandled specifier `%s' for argument #%u in format string",
|
|
spec.name,
|
|
idx
|
|
};
|
|
}
|
|
catch(const illegal &e)
|
|
{
|
|
throw illegal
|
|
{
|
|
"Specifier `%s' for argument #%u: %s",
|
|
spec.name,
|
|
idx,
|
|
e.what()
|
|
};
|
|
}
|
|
|
|
template<class T,
|
|
class lambda>
|
|
bool
|
|
fmt::visit_type(const arg &val,
|
|
lambda&& closure)
|
|
{
|
|
const auto &ptr(get<0>(val));
|
|
const auto &type(get<1>(val));
|
|
return type == typeid(T)? closure(*static_cast<const T *>(ptr)) : false;
|
|
}
|
|
|
|
bool
|
|
fmt::pointer_specifier::operator()(char *&out,
|
|
const size_t &max,
|
|
const spec &,
|
|
const arg &val)
|
|
const
|
|
{
|
|
using karma::ulong_;
|
|
using karma::eps;
|
|
using karma::maxwidth;
|
|
|
|
static const auto throw_illegal([]
|
|
{
|
|
throw illegal("Not a pointer");
|
|
});
|
|
|
|
struct generator
|
|
:karma::grammar<char *, uintptr_t()>
|
|
{
|
|
karma::rule<char *, uintptr_t()> pointer_hex
|
|
{
|
|
lit("0x") << karma::hex
|
|
};
|
|
|
|
generator(): generator::base_type{pointer_hex} {}
|
|
}
|
|
static const generator;
|
|
|
|
const auto &ptr(get<0>(val));
|
|
const auto &type(get<1>(val));
|
|
const void *const p(*static_cast<const void *const *>(ptr));
|
|
return karma::generate(out, maxwidth(max)[generator] | eps[throw_illegal], uintptr_t(p));
|
|
}
|
|
|
|
bool
|
|
fmt::char_specifier::operator()(char *&out,
|
|
const size_t &max,
|
|
const spec &,
|
|
const arg &val)
|
|
const
|
|
{
|
|
using karma::eps;
|
|
using karma::maxwidth;
|
|
|
|
static const auto throw_illegal([]
|
|
{
|
|
throw illegal("Not a printable character");
|
|
});
|
|
|
|
struct generator
|
|
:karma::grammar<char *, char()>
|
|
{
|
|
karma::rule<char *, char()> printable
|
|
{
|
|
karma::print
|
|
,"character"
|
|
};
|
|
|
|
generator(): generator::base_type{printable} {}
|
|
}
|
|
static const generator;
|
|
|
|
const auto &ptr(get<0>(val));
|
|
const auto &type(get<1>(val));
|
|
if(type == typeid(const char))
|
|
{
|
|
const auto &c(*static_cast<const char *>(ptr));
|
|
karma::generate(out, maxwidth(max)[generator] | eps[throw_illegal], c);
|
|
return true;
|
|
}
|
|
else return false;
|
|
}
|
|
|
|
bool
|
|
fmt::bool_specifier::operator()(char *&out,
|
|
const size_t &max,
|
|
const spec &,
|
|
const arg &val)
|
|
const
|
|
{
|
|
using karma::eps;
|
|
using karma::maxwidth;
|
|
|
|
static const auto throw_illegal([]
|
|
{
|
|
throw illegal("Failed to print signed value");
|
|
});
|
|
|
|
const auto closure([&](const bool &boolean)
|
|
{
|
|
using karma::maxwidth;
|
|
|
|
struct generator
|
|
:karma::grammar<char *, bool()>
|
|
{
|
|
karma::rule<char *, bool()> rule
|
|
{
|
|
karma::bool_
|
|
,"boolean"
|
|
};
|
|
|
|
generator(): generator::base_type{rule} {}
|
|
}
|
|
static const generator;
|
|
|
|
return karma::generate(out, maxwidth(max)[generator] | eps[throw_illegal], boolean);
|
|
});
|
|
|
|
return !until(types, [&](auto type)
|
|
{
|
|
return !visit_type<decltype(type)>(val, closure);
|
|
});
|
|
}
|
|
|
|
bool
|
|
fmt::signed_specifier::operator()(char *&out,
|
|
const size_t &max,
|
|
const spec &spec,
|
|
const arg &val)
|
|
const
|
|
{
|
|
static const auto throw_illegal([]
|
|
{
|
|
throw illegal("Failed to print signed value");
|
|
});
|
|
|
|
const auto closure([&out, &max, &spec, &val]
|
|
(const long &integer)
|
|
{
|
|
using karma::long_;
|
|
|
|
struct generator
|
|
:karma::grammar<char *, long()>
|
|
{
|
|
karma::rule<char *, long()> rule
|
|
{
|
|
long_
|
|
,"signed long integer"
|
|
};
|
|
|
|
_r1_type width;
|
|
karma::rule<char *, long(ushort)> aligned_left
|
|
{
|
|
karma::left_align(width)[rule]
|
|
,"left aligned"
|
|
};
|
|
|
|
karma::rule<char *, long(ushort)> aligned_right
|
|
{
|
|
karma::right_align(width)[rule]
|
|
,"right aligned"
|
|
};
|
|
|
|
karma::rule<char *, long(ushort)> aligned_center
|
|
{
|
|
karma::center(width)[rule]
|
|
,"center aligned"
|
|
};
|
|
|
|
generator(): generator::base_type{rule} {}
|
|
}
|
|
static const generator;
|
|
|
|
const auto &mw(maxwidth(max));
|
|
static const auto &ep(eps[throw_illegal]);
|
|
|
|
if(!spec.width)
|
|
return karma::generate(out, mw[generator] | ep, integer);
|
|
|
|
if(spec.sign == '-')
|
|
{
|
|
const auto &g(generator.aligned_left(spec.width));
|
|
return karma::generate(out, mw[g] | ep, integer);
|
|
}
|
|
|
|
const auto &g(generator.aligned_right(spec.width));
|
|
return karma::generate(out, mw[g] | ep, integer);
|
|
});
|
|
|
|
return !until(types, [&](auto type)
|
|
{
|
|
return !visit_type<decltype(type)>(val, closure);
|
|
});
|
|
}
|
|
|
|
bool
|
|
fmt::unsigned_specifier::operator()(char *&out,
|
|
const size_t &max,
|
|
const spec &spec,
|
|
const arg &val)
|
|
const
|
|
{
|
|
static const auto throw_illegal([]
|
|
{
|
|
throw illegal("Failed to print unsigned value");
|
|
});
|
|
|
|
const auto closure([&out, &max, &spec, &val]
|
|
(const ulong &integer)
|
|
{
|
|
using karma::ulong_;
|
|
|
|
struct generator
|
|
:karma::grammar<char *, ulong()>
|
|
{
|
|
karma::rule<char *, ulong()> rule
|
|
{
|
|
ulong_
|
|
,"unsigned long integer"
|
|
};
|
|
|
|
_r1_type width;
|
|
karma::rule<char *, ulong(ushort)> aligned_left
|
|
{
|
|
karma::left_align(width)[rule]
|
|
,"left aligned"
|
|
};
|
|
|
|
karma::rule<char *, ulong(ushort)> aligned_right
|
|
{
|
|
karma::right_align(width)[rule]
|
|
,"right aligned"
|
|
};
|
|
|
|
karma::rule<char *, ulong(ushort)> aligned_center
|
|
{
|
|
karma::center(width)[rule]
|
|
,"center aligned"
|
|
};
|
|
|
|
generator(): generator::base_type{rule} {}
|
|
}
|
|
static const generator;
|
|
|
|
const auto &mw(maxwidth(max));
|
|
static const auto &ep(eps[throw_illegal]);
|
|
|
|
if(!spec.width)
|
|
return karma::generate(out, mw[generator] | ep, integer);
|
|
|
|
if(spec.sign == '-')
|
|
{
|
|
const auto &g(generator.aligned_left(spec.width));
|
|
return karma::generate(out, mw[g] | ep, integer);
|
|
}
|
|
|
|
const auto &g(generator.aligned_right(spec.width));
|
|
return karma::generate(out, mw[g] | ep, integer);
|
|
});
|
|
|
|
return !until(types, [&](auto type)
|
|
{
|
|
return !visit_type<decltype(type)>(val, closure);
|
|
});
|
|
}
|
|
|
|
bool
|
|
fmt::hex_lowercase_specifier::operator()(char *&out,
|
|
const size_t &max,
|
|
const spec &s,
|
|
const arg &val)
|
|
const
|
|
{
|
|
static const auto throw_illegal([]
|
|
{
|
|
throw illegal("Failed to print hexadecimal value");
|
|
});
|
|
|
|
const auto closure([&](const uint &integer)
|
|
{
|
|
using karma::maxwidth;
|
|
|
|
struct generator
|
|
:karma::grammar<char *, uint()>
|
|
{
|
|
karma::rule<char *, uint()> rule
|
|
{
|
|
karma::lower[karma::hex]
|
|
,"unsigned lowercase hexadecimal"
|
|
};
|
|
|
|
generator(): generator::base_type{rule} {}
|
|
}
|
|
static const generator;
|
|
|
|
return karma::generate(out, maxwidth(max)[generator] | eps[throw_illegal], integer);
|
|
});
|
|
|
|
return !until(types, [&](auto type)
|
|
{
|
|
return !visit_type<decltype(type)>(val, closure);
|
|
});
|
|
}
|
|
|
|
bool
|
|
fmt::float_specifier::operator()(char *&out,
|
|
const size_t &max,
|
|
const spec &s,
|
|
const arg &val)
|
|
const
|
|
{
|
|
static const auto throw_illegal([]
|
|
{
|
|
throw illegal("Failed to print floating point value");
|
|
});
|
|
|
|
const auto closure([&](const double &floating)
|
|
{
|
|
using karma::double_;
|
|
using karma::maxwidth;
|
|
|
|
struct generator
|
|
:karma::grammar<char *, double()>
|
|
{
|
|
karma::rule<char *, double()> rule
|
|
{
|
|
double_
|
|
,"floating point integer"
|
|
};
|
|
|
|
generator(): generator::base_type{rule} {}
|
|
}
|
|
static const generator;
|
|
|
|
return karma::generate(out, maxwidth(max)[generator] | eps[throw_illegal], floating);
|
|
});
|
|
|
|
return !until(types, [&](auto type)
|
|
{
|
|
return !visit_type<decltype(type)>(val, closure);
|
|
});
|
|
}
|
|
|
|
bool
|
|
fmt::string_specifier::operator()(char *&out,
|
|
const size_t &max,
|
|
const spec &spec,
|
|
const arg &val)
|
|
const
|
|
{
|
|
using karma::char_;
|
|
using karma::eps;
|
|
using karma::maxwidth;
|
|
using karma::unused_type;
|
|
|
|
static const auto throw_illegal([]
|
|
{
|
|
throw illegal("Not a printable string");
|
|
});
|
|
|
|
struct generator
|
|
:karma::grammar<char *, const string_view &>
|
|
{
|
|
karma::rule<char *, const string_view &> string
|
|
{
|
|
*(karma::print)
|
|
,"string"
|
|
};
|
|
|
|
_r1_type width;
|
|
karma::rule<char *, const string_view &(ushort)> aligned_left
|
|
{
|
|
karma::left_align(width)[string]
|
|
,"left aligned"
|
|
};
|
|
|
|
karma::rule<char *, const string_view &(ushort)> aligned_right
|
|
{
|
|
karma::right_align(width)[string]
|
|
,"right aligned"
|
|
};
|
|
|
|
karma::rule<char *, const string_view &(ushort)> aligned_center
|
|
{
|
|
karma::center(width)[string]
|
|
,"center aligned"
|
|
};
|
|
|
|
generator() :generator::base_type{string} {}
|
|
}
|
|
static const generator;
|
|
|
|
const auto &mw(maxwidth(max));
|
|
static const auto &ep(eps[throw_illegal]);
|
|
|
|
if(!spec.width)
|
|
return generate_string(out, mw[generator] | ep, val);
|
|
|
|
if(spec.sign == '-')
|
|
{
|
|
const auto &g(generator.aligned_left(spec.width));
|
|
return generate_string(out, mw[g] | ep, val);
|
|
}
|
|
|
|
const auto &g(generator.aligned_right(spec.width));
|
|
return generate_string(out, mw[g] | ep, val);
|
|
}
|
|
|
|
template<class generator>
|
|
bool
|
|
fmt::generate_string(char *&out,
|
|
const generator &gen,
|
|
const arg &val)
|
|
{
|
|
using karma::eps;
|
|
|
|
const auto &ptr(get<0>(val));
|
|
const auto &type(get<1>(val));
|
|
if(type == typeid(ircd::string_view) ||
|
|
type == typeid(ircd::json::string) ||
|
|
type == typeid(ircd::json::object) ||
|
|
type == typeid(ircd::json::array))
|
|
{
|
|
const auto &str(*static_cast<const ircd::string_view *>(ptr));
|
|
return karma::generate(out, gen, str);
|
|
}
|
|
else if(type == typeid(std::string_view))
|
|
{
|
|
const auto &str(*static_cast<const std::string_view *>(ptr));
|
|
return karma::generate(out, gen, str);
|
|
}
|
|
else if(type == typeid(std::string))
|
|
{
|
|
const auto &str(*static_cast<const std::string *>(ptr));
|
|
return karma::generate(out, gen, string_view{str});
|
|
}
|
|
else if(type == typeid(const char *))
|
|
{
|
|
const char *const &str{*static_cast<const char *const *const>(ptr)};
|
|
return karma::generate(out, gen, string_view{str});
|
|
}
|
|
|
|
// This for string literals which have unique array types depending on their size.
|
|
// There is no reasonable way to match them. The best that can be hoped for is the
|
|
// grammar will fail gracefully (most of the time) or not print something bogus when
|
|
// it happens to be legal.
|
|
const auto &str(static_cast<const char *>(ptr));
|
|
return karma::generate(out, gen, string_view{str});
|
|
}
|