#ifndef TOML11_FORMAT #define TOML11_FORMAT #include "value.hpp" #include #include #include #include #include namespace toml { // synopsis // toml::format("key", value, toml::make_inline(80)) // toml::format("key", value, toml::forceinline) // std::cout << toml::make_inline(80) << value; // std::cout << toml::forceinline << value; template, typename alloc = std::allocator> std::basic_string format(const value& v); template, typename alloc = std::allocator> std::basic_string format(const value& v, std::size_t mk); template, typename alloc = std::allocator> std::basic_string format(const toml::key& k, const value& v); template, typename alloc = std::allocator> std::basic_string format(const toml::key& k, const value& v, std::size_t mk); template struct format_impl; template<> struct format_impl { typedef detail::toml_default_type::type type; std::basic_string operator()(const type& val) { return val ? "true" : "false"; } }; template<> struct format_impl { typedef detail::toml_default_type::type type; std::basic_string operator()(const type& val) { return std::to_string(val); } }; template<> struct format_impl { typedef detail::toml_default_type::type type; std::basic_string operator()(const type& val) { std::basic_ostringstream oss; oss << std::showpoint << val; if(oss.str().back() == '.') oss << '0'; return oss.str(); } }; template<> struct format_impl { typedef detail::toml_default_type::type type; std::size_t max_length; format_impl() : max_length(80){} format_impl(std::size_t mx) : max_length(mx){} std::basic_string operator()(const type& val) { auto tmp = make_inline(val); if(max_length == std::numeric_limits::max() || tmp.size() <= max_length) return tmp; return convert_multiline(std::move(tmp)); } private: std::basic_string make_inline(const std::basic_string& val) { std::basic_string str; str += '"'; for(const auto& c : val) { if('\0' < c && c < '\31') { switch(c) { case '\b': str += "\\b"; break; case '\t': str += "\\t"; break; case '\n': str += "\\n"; break; case '\f': str += "\\f"; break; case '\r': str += "\\r"; break; default: { str += 'u'; std::basic_ostringstream oss; oss << std::setw(4) << std::setfill('0') << std::hex << static_cast(c); auto hexdig = oss.str(); std::transform(hexdig.begin(), hexdig.end(), hexdig.begin(), ::toupper); str += oss.str(); break; } } } else if(c == '"') { str += "\\\""; } else if(c == '\\') { str += "\\\\"; } else { str += c; } } str += '"'; return str; } std::basic_string convert_multiline(std::basic_string&& val) { std::basic_string str; str.reserve(val.size() + 6); str += "\"\"\"\n"; std::size_t current = 0; for(auto iter = val.begin()+1; iter != val.end()-1; ++iter) { if(*iter != '\\') { if(current + 1 == max_length) str += "\\\n"; str += *iter; continue; } assert(std::next(iter) < val.end()-1); if(*std::next(iter) == 'u') { if(current + 5 == max_length) str += "\\\n"; assert(iter + 5 < val.end()-1); str += *iter; ++iter; // u str += *iter; ++iter; // 0 str += *iter; ++iter; // 1 str += *iter; ++iter; // 2 str += *iter; continue;// 3 } if(current + 2 == max_length) str += "\\\n"; str += *iter; ++iter; str += *iter; } str += "\"\"\""; return str; } }; template<> struct format_impl { typedef detail::toml_default_type::type type; std::basic_string operator()(const type& val) { std::basic_ostringstream oss; oss << val; return oss.str(); } }; // TODO max length! template<> struct format_impl { typedef detail::toml_default_type::type type; std::size_t max_length; format_impl() : max_length(80){} format_impl(std::size_t mx) : max_length(mx){} std::basic_string operator()(const type& val) { std::basic_string retval; retval += '['; for(const auto& item : val) { auto tmp = format(val, max_length - 1); retval += tmp; retval += ", "; if(tmp.size() * 2 > max_length) retval += '\n'; } retval += ']'; return retval; } }; // TODO max length && inline! template<> struct format_impl { typedef detail::toml_default_type::type type; std::size_t max_length; format_impl() : max_length(80){} format_impl(std::size_t mx) : max_length(mx){} std::basic_string operator()(const type& val) { std::basic_string retval; for(const auto& item : val) { retval += item.first; retval += " = "; retval += format(item.second); retval += '\n'; } return retval; } }; template std::basic_string format(const value& v) { switch(v.type()) { case value_t::Boolean : return format_impl{}(v.template cast()); case value_t::Integer : return format_impl{}(v.template cast()); case value_t::Float : return format_impl{}(v.template cast()); case value_t::String : return format_impl{}(v.template cast()); case value_t::Datetime: return format_impl{}(v.template cast()); case value_t::Array : return format_impl{}(v.template cast()); case value_t::Table : return format_impl{}(v.template cast()); case value_t::Empty : throw std::runtime_error("toml::format: empty value"); case value_t::Unknown : throw std::runtime_error("toml::format: unknown value"); default: throw std::logic_error("toml::format: unknown enum value"); } } template std::basic_string format(const value& v, std::size_t inl) { switch(v.type()) { case value_t::Boolean : return format_impl{}(v.template cast()); case value_t::Integer : return format_impl{}(v.template cast()); case value_t::Float : return format_impl{}(v.template cast()); case value_t::String : return format_impl{inl}(v.template cast()); case value_t::Datetime: return format_impl{}(v.template cast()); case value_t::Array : return format_impl{inl}(v.template cast()); case value_t::Table : return format_impl{inl}(v.template cast()); case value_t::Empty : throw std::runtime_error("toml::format: empty value"); case value_t::Unknown : throw std::runtime_error("toml::format: unknown value"); default: throw std::logic_error("toml::format: unknown enum value"); } } template std::basic_string format(std::basic_string key, const value& val) { std::basic_string retval(std::move(key)); retval += " = "; retval += format(val); return retval; } template std::basic_string format(std::basic_string key, const value& val, std::size_t mk) { std::basic_string retval(std::move(key)); retval += " = "; retval += format(val, mk); return retval; } // ----------------------------- stream operators ----------------------------- namespace detail { template struct inline_limit { static_assert(std::is_same::value, "do not instantiate this"); static const int index; T limit; inline_limit() = default; ~inline_limit() = default; constexpr inline_limit(T i): limit(i){} constexpr operator T() const {return limit;} static void callback(std::ios_base::event ev, std::ios_base& ios, int idx) { void*& info = ios.pword(idx); switch (ev) { case std::ios_base::erase_event: { delete static_cast(info); break; } case std::ios_base::copyfmt_event: { info = new std::size_t(*static_cast(info)); break; } case std::ios_base::imbue_event: { break; } } } }; template const int inline_limit::index = std::ios_base::xalloc(); } //detail template> std::basic_ostream& operator<<(std::basic_ostream& os, const detail::inline_limit& inl) { void*& info = os.pword(detail::inline_limit::index); if(!os.bad()) { if(info == nullptr) { os.register_callback(detail::inline_limit::callback, detail::inline_limit::index); info = new std::size_t(inl.limit); } else { *static_cast(info) = inl.limit; } } return os; } constexpr static detail::inline_limit forceinline( std::numeric_limits::max()); inline detail::inline_limit make_inline(std::size_t sz) { return detail::inline_limit(sz); } template> std::basic_ostream& operator<<(std::basic_ostream& os, const toml::value& v) { std::size_t* info = static_cast(os.pword(detail::inline_limit::index)); return os << (info == nullptr ? toml::format(v) : toml::format(v, *info)); } } #endif // TOML11_FORMAT