rexylib/include/rexy/detail/format/context_handler.tpp

163 lines
5.6 KiB
C++

/**
This file is a part of rexy's general purpose library
Copyright (C) 2022 rexy712
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 <http://www.gnu.org/licenses/>.
*/
#ifndef REXY_DETAIL_FORMAT_CONTEXT_HANDLER_TPP
#define REXY_DETAIL_FORMAT_CONTEXT_HANDLER_TPP
#include "context_handler.hpp"
#include "parse_context.hpp"
#include "format_context.hpp"
#include "formatter.hpp"
#include "format_args.hpp"
#include "named_args.hpp"
#include "format_error.hpp"
#include "../../string_view.hpp"
#include <locale> //locale
#include <cstddef> //size_t
#include <type_traits> //remove_cvref
namespace rexy::fmt::detail{
///////////////////////////////format_handler//////////////////////////////////
template<class Char>
format_handler<Char>::format_handler(out_it_t output, basic_string_view<char_type> fmt, basic_format_args<fmt_ctx_t> args):
fmt_ctx(args, output),
parse_ctx(fmt){}
template<class Char>
format_handler<Char>::format_handler(out_it_t output, basic_string_view<char_type> fmt, basic_format_args<fmt_ctx_t> args, const std::locale& l):
fmt_ctx(args, output, l),
parse_ctx(fmt){}
template<class Char>
constexpr auto format_handler<Char>::do_raw(fmt_it_t start, fmt_it_t fin) -> fmt_it_t{
for(auto it = start;it != fin;++it){
fmt_ctx.out() = *it;
}
return fin;
}
template<class Char>
constexpr auto format_handler<Char>::do_format_spec(fmt_it_t start, fmt_it_t fin, std::size_t id) -> fmt_it_t{
parse_ctx.advance_to(start);
const auto arg = fmt_ctx.arg(id);
format_specs specs;
visit_format_arg(format::arg_formatter<fmt_ctx_t,parse_ctx_t,format_specs>{fmt_ctx, parse_ctx, specs}, arg);
return parse_ctx.begin();
}
template<class Char>
constexpr auto format_handler<Char>::do_empty_format(fmt_it_t start, fmt_it_t last, std::size_t id) -> fmt_it_t{
parse_ctx.advance_to(start);
const auto arg = fmt_ctx.arg(id);
visit_format_arg(format::empty_formatter<fmt_ctx_t,parse_ctx_t>{fmt_ctx, parse_ctx}, arg);
return parse_ctx.begin();
}
template<class Char>
constexpr std::size_t format_handler<Char>::on_arg_id(void){
return parse_ctx.next_arg_id();
}
template<class Char>
constexpr std::size_t format_handler<Char>::on_arg_id(std::size_t i){
parse_ctx.check_arg_id(i);
return i;
}
template<class Char>
constexpr std::size_t format_handler<Char>::on_arg_id(fmt_it_t start, fmt_it_t last){
const auto id = fmt_ctx.arg_index(start, last);
parse_ctx.check_arg_id(id);
return id;
}
///////////////////////////////format_checker//////////////////////////////////
template<class Char, class... Args>
consteval format_checker<Char,Args...>::format_checker(basic_string_view<char_type> fmt, std::size_t numargs):
parse_ctx(fmt, numargs){}
template<class Char, class... Args>
constexpr auto format_checker<Char,Args...>::do_raw(fmt_it_t, fmt_it_t last) -> fmt_it_t{
return last;
}
template<class Char, class... Args>
constexpr auto format_checker<Char,Args...>::do_format_spec(fmt_it_t start, fmt_it_t last, std::size_t id) -> fmt_it_t{
parse_ctx.advance_to(start);
if constexpr(sizeof...(Args)){
return do_format_spec_impl<0,Args...>(start, last, id);
}else{
REXY_THROW_FORMAT_ERROR("Missing argument");
return start;
}
}
template<class Char, class... Args>
constexpr void format_checker<Char,Args...>::do_empty_format(fmt_it_t, fmt_it_t, std::size_t){/*nothing to parse and the checker doesn't format so just return*/}
template<class Char, class... Args>
template<std::size_t I, class FArg, class... FArgs>
constexpr auto format_checker<Char,Args...>::do_format_spec_impl(fmt_it_t start, fmt_it_t last, std::size_t id) -> fmt_it_t{
if(I == id){
using fmt_ctx_t = fmt_context_t<Char>;
using base_type = std::remove_cvref_t<FArg>;
using stored_type = stored_type_t<base_type,fmt_ctx_t>;
if constexpr(Handle<stored_type,fmt_ctx_t>){
using fmter_t = typename fmt_ctx_t::template formatter_type<base_type>;
fmter_t fmter{};
return fmter.parse(parse_ctx);
}else{
dynamic_format_specs<Char> specs{};
format_specs_checker<cx_format_specs_handler<parse_ctx_t,Args...>> handler{
cx_format_specs_handler<parse_ctx_t,Args...>{parse_ctx, specs},
detail::map_to_storage_enum_v<stored_type,Char>
};
return parse::perform_standard_parse(start, last, handler);
}
}else{
if constexpr(sizeof...(FArgs) > 0){
return do_format_spec_impl<I+1,FArgs...>(start, last, id);
}
}
REXY_THROW_FORMAT_ERROR("Missing argument");
}
template<class Char, class... Args>
constexpr std::size_t format_checker<Char,Args...>::on_arg_id(void){
return parse_ctx.next_arg_id();
}
template<class Char, class... Args>
constexpr std::size_t format_checker<Char,Args...>::on_arg_id(std::size_t i){
if(i > sizeof...(Args)){
REXY_THROW_FORMAT_ERROR("Arg index out of bounds");
}
parse_ctx.check_arg_id(i);
return i;
}
template<class Char, class... Args>
constexpr std::size_t format_checker<Char,Args...>::on_arg_id(fmt_it_t start, fmt_it_t last){
const std::size_t id = find_static_named_arg_id<fmt_it_t, Args...>(start, last);
if(id == invalid_arg_index){
REXY_THROW_FORMAT_ERROR("No such named arg");
}
return on_arg_id(id);
}
}
#endif