mirror of
https://github.com/marzer/tomlplusplus.git
synced 2026-02-26 17:22:13 +00:00
added printing for arrays
also - added many member functions to `array` - added more documentation - added format_flags - added some additional cleaning steps to `generate_single_header.py` - made formatters work for any node type, not just tables - fixed documentation header obscuring content during jumps
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "toml_common.h"
|
||||
#include "toml_date_time.h"
|
||||
#include "toml_print_to_stream.h"
|
||||
#include "toml_node.h"
|
||||
#include "toml_table.h"
|
||||
#include "toml_array.h"
|
||||
@@ -13,7 +14,6 @@
|
||||
#include "toml_node_view.h"
|
||||
#include "toml_utf8.h"
|
||||
#include "toml_parser.h"
|
||||
#include "toml_print_to_stream.h"
|
||||
#include "toml_formatter.h"
|
||||
#include "toml_default_formatter.h"
|
||||
#include "toml_json_formatter.h"
|
||||
@@ -51,4 +51,5 @@
|
||||
#undef TOML_STRING_PREFIX_1
|
||||
#undef TOML_STRING_PREFIX
|
||||
#undef TOML_UNDEF_MACROS
|
||||
#undef TOML_DOXYGEN
|
||||
#endif
|
||||
|
||||
@@ -79,6 +79,25 @@ namespace toml::impl
|
||||
|
||||
namespace toml
|
||||
{
|
||||
/// \brief A TOML array.
|
||||
/// \detail The interface of this type is modeled after std::vector so things
|
||||
/// mostly work as you'd expect them to with a vector: \cpp
|
||||
///
|
||||
/// auto table = toml::parse("arr = [1, 2, 3, 4, 'five']"sv);
|
||||
///
|
||||
/// auto& arr = *table.get_as<toml::array>("arr");
|
||||
///
|
||||
/// for (size_t i = 0; i < arr.size(); i++)
|
||||
/// {
|
||||
/// arr[i].visit([=](auto&& el) noexcept
|
||||
/// {
|
||||
/// std::cout << el << ", ";
|
||||
/// });
|
||||
/// }
|
||||
///
|
||||
/// // prints: 1, 2, 3, 4, "five"
|
||||
///
|
||||
/// \ecpp
|
||||
class array final
|
||||
: public node
|
||||
{
|
||||
@@ -88,6 +107,11 @@ namespace toml
|
||||
|
||||
public:
|
||||
|
||||
using value_type = node;
|
||||
using size_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
using reference = node&;
|
||||
using const_reference = const node&;
|
||||
using iterator = impl::array_iterator<false>;
|
||||
using const_iterator = impl::array_iterator<true>;
|
||||
|
||||
@@ -108,23 +132,20 @@ namespace toml
|
||||
}
|
||||
|
||||
[[nodiscard]] node_type type() const noexcept override { return node_type::array; }
|
||||
|
||||
[[nodiscard]] bool is_table() const noexcept override { return false; }
|
||||
[[nodiscard]] bool is_array() const noexcept override { return true; }
|
||||
[[nodiscard]] bool is_value() const noexcept override { return false; }
|
||||
[[nodiscard]] array* as_array() noexcept override { return this; }
|
||||
[[nodiscard]] const array* as_array() const noexcept override { return this; }
|
||||
|
||||
[[nodiscard]] bool is_array_of_tables() const noexcept override
|
||||
{
|
||||
if (values.empty())
|
||||
return false;
|
||||
|
||||
for (auto& val : values)
|
||||
if (!val->is_table())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// \brief Checks if the array contains values of only one type.
|
||||
///
|
||||
/// \tparam T A TOML node type. Provide an explicit type for "is every element a T?".
|
||||
/// Leave it as the default `void` for "is every element the same type?".
|
||||
///
|
||||
/// \returns True if the array was homogeneous.
|
||||
///
|
||||
/// \attention Empty arrays are _not_ regarded as homogeneous.
|
||||
template <typename T = void>
|
||||
[[nodiscard]] bool is_homogeneous() const noexcept
|
||||
{
|
||||
@@ -147,34 +168,102 @@ namespace toml
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] array* as_array() noexcept override { return this; }
|
||||
[[nodiscard]] const array* as_array() const noexcept override { return this; }
|
||||
/// \brief Returns true if this array contains only tables.
|
||||
[[nodiscard]] TOML_ALWAYS_INLINE
|
||||
bool is_array_of_tables() const noexcept override
|
||||
{
|
||||
return is_homogeneous<toml::table>();
|
||||
}
|
||||
|
||||
/// \brief Gets a reference to the node element at a specific index.
|
||||
[[nodiscard]] node& operator[] (size_t index) noexcept { return *values[index]; }
|
||||
/// \brief Gets a reference to the node element at a specific index.
|
||||
[[nodiscard]] const node& operator[] (size_t index) const noexcept { return *values[index]; }
|
||||
|
||||
/// \brief Returns a reference to the first element in the array.
|
||||
[[nodiscard]] node& front() noexcept { return *values.front(); }
|
||||
/// \brief Returns a reference to the first element in the array.
|
||||
[[nodiscard]] const node& front() const noexcept { return *values.front(); }
|
||||
/// \brief Returns a reference to the last element in the array.
|
||||
[[nodiscard]] node& back() noexcept { return *values.back(); }
|
||||
/// \brief Returns a reference to the last element in the array.
|
||||
[[nodiscard]] const node& back() const noexcept { return *values.back(); }
|
||||
|
||||
/// \brief Returns an iterator to the first element.
|
||||
[[nodiscard]] iterator begin() noexcept { return { values.begin() }; }
|
||||
/// \brief Returns an iterator to the first element.
|
||||
[[nodiscard]] const_iterator begin() const noexcept { return { values.begin() }; }
|
||||
/// \brief Returns an iterator to the first element.
|
||||
[[nodiscard]] const_iterator cbegin() const noexcept { return { values.cbegin() }; }
|
||||
|
||||
/// \brief Returns an iterator to one-past-the-last element.
|
||||
[[nodiscard]] iterator end() noexcept { return { values.end() }; }
|
||||
/// \brief Returns an iterator to one-past-the-last element.
|
||||
[[nodiscard]] const_iterator end() const noexcept { return { values.end() }; }
|
||||
/// \brief Returns an iterator to one-past-the-last element.
|
||||
[[nodiscard]] const_iterator cend() const noexcept { return { values.cend() }; }
|
||||
|
||||
/// \brief Returns true if the array is empty.
|
||||
[[nodiscard]] bool empty() const noexcept { return values.empty(); }
|
||||
/// \brief Returns the number of elements in the array.
|
||||
[[nodiscard]] size_t size() const noexcept { return values.size(); }
|
||||
/// \brief Reserves internal storage capacity up to a pre-determined number of elements.
|
||||
void reserve(size_t new_capacity) TOML_MAY_THROW { values.reserve(new_capacity); }
|
||||
|
||||
[[nodiscard]] node& operator[] (size_t index) & noexcept { return *values[index].get(); }
|
||||
[[nodiscard]] node&& operator[] (size_t index) && noexcept { return std::move(*values[index].get()); }
|
||||
[[nodiscard]] const node& operator[] (size_t index) const& noexcept { return *values[index].get(); }
|
||||
|
||||
/// \brief Removes all elements from the array.
|
||||
void clear() noexcept { values.clear(); }
|
||||
|
||||
// insert()
|
||||
// emplace()
|
||||
|
||||
/// \brief Removes the specified element from the array.
|
||||
///
|
||||
/// \returns The position following the Iterator following the removed element.
|
||||
iterator erase(const_iterator pos) noexcept
|
||||
{
|
||||
return iterator{ values.erase(pos.raw_) };
|
||||
}
|
||||
|
||||
/// \brief Removes the specified range of elements elements from the array.
|
||||
///
|
||||
/// \returns The position following the Iterator following the last removed element.
|
||||
iterator erase(const_iterator first, const_iterator last) noexcept
|
||||
{
|
||||
return iterator{ values.erase(first.raw_, last.raw_) };
|
||||
}
|
||||
|
||||
// push_back()
|
||||
// emplace_back()
|
||||
|
||||
/// \brief Removes the last element from the array.
|
||||
void pop_back() noexcept { values.pop_back(); }
|
||||
|
||||
/// \brief Gets an element at a specific index if it is a particular type.
|
||||
///
|
||||
/// \tparam T The node's type.
|
||||
/// \param index The element index.
|
||||
///
|
||||
/// \returns A pointer to the selected element if it was of the specified type, or nullptr.
|
||||
template <typename T>
|
||||
[[nodiscard]] node_of<T>* get_as(size_t index) noexcept
|
||||
{
|
||||
return values[index]->as<T>();
|
||||
}
|
||||
|
||||
/// \brief Gets an element at a specific index if it is a particular type.
|
||||
///
|
||||
/// \tparam T The node's type.
|
||||
/// \param index The element index.
|
||||
///
|
||||
/// \returns A pointer to the selected element if it was of the specified type, or nullptr.
|
||||
template <typename T>
|
||||
[[nodiscard]] const node_of<T>* get_as(size_t index) const noexcept
|
||||
{
|
||||
return values[index]->as<T>();
|
||||
}
|
||||
|
||||
[[nodiscard]] iterator begin() noexcept { return { values.begin() }; }
|
||||
[[nodiscard]] const_iterator begin() const noexcept { return { values.begin() }; }
|
||||
[[nodiscard]] const_iterator cbegin() const noexcept { return { values.cbegin() }; }
|
||||
|
||||
[[nodiscard]] iterator end() noexcept { return { values.end() }; }
|
||||
[[nodiscard]] const_iterator end() const noexcept { return { values.end() }; }
|
||||
[[nodiscard]] const_iterator cend() const noexcept { return { values.cend() }; }
|
||||
template <typename CHAR>
|
||||
friend inline std::basic_ostream<CHAR>& operator << (std::basic_ostream<CHAR>&, const array&) TOML_MAY_THROW;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -114,8 +114,6 @@
|
||||
#define TOML_USE_STREAMS_FOR_FLOATS 1
|
||||
#endif
|
||||
|
||||
#elif defined (DOXYGEN)
|
||||
#define TOML_EXCEPTIONS 0
|
||||
#endif
|
||||
|
||||
#ifndef TOML_CPP_VERSION
|
||||
@@ -138,6 +136,9 @@
|
||||
#ifndef TOML_EXCEPTIONS
|
||||
#define TOML_EXCEPTIONS 1
|
||||
#endif
|
||||
#ifndef TOML_DOXYGEN
|
||||
#define TOML_DOXYGEN 0
|
||||
#endif
|
||||
#if TOML_EXCEPTIONS
|
||||
#define TOML_CONDITIONAL_NOEXCEPT(...) noexcept(__VA_ARGS__)
|
||||
#define TOML_MAY_THROW
|
||||
@@ -273,20 +274,17 @@ TOML_POP_WARNINGS
|
||||
/// \brief The root namespace for all toml++ functions and types.
|
||||
namespace toml
|
||||
{
|
||||
/// \brief User-defined literals.
|
||||
inline namespace literals
|
||||
{
|
||||
using namespace std::string_literals;
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
/// \brief Specifies a uint8_t literal.
|
||||
[[nodiscard]] TOML_ALWAYS_INLINE
|
||||
TOML_CONSTEVAL uint8_t operator"" _u8(unsigned long long n) noexcept
|
||||
{
|
||||
return static_cast<uint8_t>(n);
|
||||
}
|
||||
|
||||
/// \brief Specifies a size_t literal.
|
||||
[[nodiscard]] TOML_ALWAYS_INLINE
|
||||
TOML_CONSTEVAL size_t operator"" _sz(unsigned long long n) noexcept
|
||||
{
|
||||
@@ -302,16 +300,16 @@ namespace toml
|
||||
|
||||
#else
|
||||
|
||||
/// \brief The base character type for toml++ keys and string values.
|
||||
/// \remarks This will be `char8_t` if `TOML_CHAR_8_STRINGS = 1`, otherwise it will be `char`.
|
||||
/// \brief The base character type for keys and string values.
|
||||
/// \attention This will be `char8_t` if `TOML_CHAR_8_STRINGS` is `1`.
|
||||
using string_char = char;
|
||||
|
||||
/// \brief The string type for toml++ keys and string values.
|
||||
/// \remarks This will be `std::u8string` if `TOML_CHAR_8_STRINGS = 1`, otherwise it will be `std::string`.
|
||||
/// \brief The string type for keys and string values.
|
||||
/// \attention This will be `std::u8string` if `TOML_CHAR_8_STRINGS` is `1`.
|
||||
using string = std::string;
|
||||
|
||||
/// \brief The string type for toml++ keys and string values.
|
||||
/// \remarks This will be `std::u8string_view` if `TOML_CHAR_8_STRINGS = 1`, otherwise it will be `std::string_view`.
|
||||
/// \brief The string type for keys and string values.
|
||||
/// \attention This will be `std::u8string_view` if `TOML_CHAR_8_STRINGS` is `1`.
|
||||
using string_view = std::string_view;
|
||||
|
||||
#endif
|
||||
@@ -348,13 +346,13 @@ namespace toml
|
||||
#else
|
||||
|
||||
/// \brief The integer type used to tally line numbers and columns.
|
||||
/// \remarks This will be `uint32_t` if `TOML_LARGE_FILES = 1`, otherwise it will be `uint16_t`.
|
||||
/// \attention This will be `uint32_t` if `TOML_LARGE_FILES` is `1`.
|
||||
using source_index = uint16_t;
|
||||
|
||||
#endif
|
||||
|
||||
/// \brief A source document line-and-column pair.
|
||||
struct source_position final
|
||||
struct source_position
|
||||
{
|
||||
/// \brief The line number.
|
||||
/// \remarks Valid line numbers start at 1.
|
||||
@@ -371,7 +369,6 @@ namespace toml
|
||||
return line > source_index{} && column > source_index{};
|
||||
}
|
||||
|
||||
|
||||
[[nodiscard]]
|
||||
friend constexpr bool operator == (const source_position& lhs, const source_position& rhs) noexcept
|
||||
{
|
||||
@@ -412,7 +409,7 @@ namespace toml
|
||||
using source_path_ptr = std::shared_ptr<const std::string>;
|
||||
|
||||
/// \brief A source document region.
|
||||
struct source_region final
|
||||
struct source_region
|
||||
{
|
||||
/// \brief The beginning of the region (inclusive).
|
||||
source_position begin;
|
||||
@@ -429,7 +426,7 @@ namespace toml
|
||||
TOML_PUSH_WARNINGS
|
||||
TOML_DISABLE_INIT_WARNINGS
|
||||
|
||||
#if TOML_EXCEPTIONS
|
||||
#if !TOML_DOXYGEN && TOML_EXCEPTIONS
|
||||
|
||||
class parse_error final
|
||||
: public std::runtime_error
|
||||
@@ -472,8 +469,8 @@ namespace toml
|
||||
|
||||
/// \brief An error thrown/returned when parsing fails.
|
||||
///
|
||||
/// \remarks This will inherit from `std::runtime_exception` when `TOML_EXCEPTIONS = 1`.
|
||||
/// The public interface will be exactly the same either way.
|
||||
/// \remarks This class inherits from `std::runtime_error` when `TOML_EXCEPTIONS` is `1`.
|
||||
/// The public interface remains the same regardless of exception mode.
|
||||
class parse_error final
|
||||
{
|
||||
private:
|
||||
@@ -537,6 +534,13 @@ namespace toml::impl
|
||||
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] TOML_ALWAYS_INLINE
|
||||
constexpr std::underlying_type_t<T> unwrap_enum(T val) noexcept
|
||||
{
|
||||
return static_cast<std::underlying_type_t<T>>(val);
|
||||
}
|
||||
|
||||
// Q: "why not use std::find??"
|
||||
// A: Because <algorithm> is _huge_ and std::find would be the only thing I used from it.
|
||||
// I don't want to impose such a heavy burden on users.
|
||||
@@ -551,20 +555,6 @@ namespace toml::impl
|
||||
return {};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct is_generic_invocable
|
||||
{
|
||||
template <typename U>
|
||||
static constexpr auto test(U&&) -> decltype(std::declval<T>()(std::declval<U&&>()), std::true_type{});
|
||||
static constexpr std::false_type test(...);
|
||||
|
||||
struct tester {};
|
||||
static constexpr auto value = decltype(test(tester{}))::value;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline constexpr bool is_generic_invocable_v = is_generic_invocable<T>::value;
|
||||
|
||||
class parser;
|
||||
|
||||
template <typename T>
|
||||
@@ -692,12 +682,17 @@ namespace toml::impl
|
||||
|
||||
namespace toml
|
||||
{
|
||||
|
||||
/// \brief Helper alias that wraps a type up as it's TOML node equivalent.
|
||||
template <typename T>
|
||||
using node_of = typename impl::node_wrapper<T>::type;
|
||||
|
||||
/// \brief Helper alias that unwraps TOML node type to it's raw value equivalent.
|
||||
template <typename T>
|
||||
using value_of = typename impl::node_unwrapper<T>::type;
|
||||
|
||||
|
||||
/// \brief Pretty-prints the value of a node_type to a stream.
|
||||
template <typename CHAR>
|
||||
inline std::basic_ostream<CHAR>& operator << (std::basic_ostream<CHAR>& lhs, node_type rhs) TOML_MAY_THROW
|
||||
{
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
|
||||
namespace toml::impl
|
||||
{
|
||||
TOML_PUSH_WARNINGS
|
||||
TOML_DISABLE_ALL_WARNINGS
|
||||
|
||||
[[nodiscard]]
|
||||
inline toml::string default_formatter_make_key_segment(const toml::string& str) noexcept
|
||||
{
|
||||
@@ -32,7 +35,16 @@ namespace toml::impl
|
||||
s.reserve(str.length() + 2_sz);
|
||||
s += TOML_STRING_PREFIX('"');
|
||||
for (auto c : str)
|
||||
s.append(escape_string_character(c));
|
||||
{
|
||||
if (c >= TOML_STRING_PREFIX('\x00') && c <= TOML_STRING_PREFIX('\x1F')) TOML_UNLIKELY
|
||||
s.append(low_character_escape_table[c]);
|
||||
else if (c == TOML_STRING_PREFIX('\x7F')) TOML_UNLIKELY
|
||||
s.append(TOML_STRING_PREFIX("\\u007F"sv));
|
||||
else if (c == TOML_STRING_PREFIX('"')) TOML_UNLIKELY
|
||||
s.append(TOML_STRING_PREFIX("\\\""sv));
|
||||
else
|
||||
s += c;
|
||||
}
|
||||
s += TOML_STRING_PREFIX('"');
|
||||
return s;
|
||||
}
|
||||
@@ -41,6 +53,8 @@ namespace toml::impl
|
||||
}
|
||||
}
|
||||
|
||||
TOML_POP_WARNINGS
|
||||
|
||||
[[nodiscard]]
|
||||
inline size_t default_formatter_inline_columns(const node& node) noexcept
|
||||
{
|
||||
@@ -177,7 +191,7 @@ namespace toml
|
||||
const auto original_indent = base::indent();
|
||||
const auto multiline = impl::default_formatter_forces_multiline(
|
||||
arr,
|
||||
base::indent_columns() * static_cast<size_t>(original_indent < 0 ? 0 : original_indent)
|
||||
base::indent_columns * static_cast<size_t>(original_indent < 0 ? 0 : original_indent)
|
||||
);
|
||||
impl::print_to_stream("["sv, base::stream());
|
||||
if (multiline)
|
||||
@@ -341,17 +355,37 @@ namespace toml
|
||||
}
|
||||
}
|
||||
|
||||
void print() TOML_MAY_THROW
|
||||
{
|
||||
switch (auto source_type = base::source().type())
|
||||
{
|
||||
case node_type::table:
|
||||
{
|
||||
auto& tbl = *reinterpret_cast<const table*>(&base::source());
|
||||
if (tbl.is_inline() || (base::flags() & format_flags::always_print_as_inline) != format_flags::none)
|
||||
print_inline(tbl);
|
||||
else
|
||||
{
|
||||
base::decrease_indent(); // so root kvps and tables have the same indent
|
||||
print(tbl);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case node_type::array:
|
||||
print(*reinterpret_cast<const array*>(&base::source()));
|
||||
break;
|
||||
|
||||
default:
|
||||
base::print(base::source(), source_type);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
TOML_NODISCARD_CTOR
|
||||
default_formatter(const toml::table& source_, toml::string_view indent_string = {}) noexcept
|
||||
: base{
|
||||
source_,
|
||||
impl::formatter_options{
|
||||
indent_string,
|
||||
false //quote_dates_and_times
|
||||
}
|
||||
}
|
||||
explicit default_formatter(const toml::node& source, format_flags flags = {}) noexcept
|
||||
: base{ source, flags }
|
||||
{}
|
||||
|
||||
template <typename T>
|
||||
@@ -359,9 +393,8 @@ namespace toml
|
||||
TOML_MAY_THROW
|
||||
{
|
||||
rhs.attach(lhs);
|
||||
rhs.base::decrease_indent(); //starts at -1 so root kvps and first-level child tables have the same indent
|
||||
rhs.key_path.clear();
|
||||
rhs.print(rhs.source());
|
||||
rhs.print();
|
||||
rhs.detach();
|
||||
return lhs;
|
||||
}
|
||||
@@ -413,4 +446,10 @@ namespace toml
|
||||
{
|
||||
return lhs << default_formatter<CHAR>{ rhs };
|
||||
}
|
||||
|
||||
template <typename CHAR>
|
||||
inline std::basic_ostream<CHAR>& operator << (std::basic_ostream<CHAR>& lhs, const array& rhs) TOML_MAY_THROW
|
||||
{
|
||||
return lhs << default_formatter<CHAR>{ rhs };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,53 +1,45 @@
|
||||
#pragma once
|
||||
#include "toml_print_to_stream.h"
|
||||
|
||||
namespace toml
|
||||
{
|
||||
enum class format_flags : uint8_t
|
||||
{
|
||||
none,
|
||||
always_print_as_inline = 1,
|
||||
quote_dates_and_times = 2
|
||||
};
|
||||
[[nodiscard]] constexpr format_flags operator & (format_flags lhs, format_flags rhs) noexcept
|
||||
{
|
||||
return static_cast<format_flags>(impl::unwrap_enum(lhs) & impl::unwrap_enum(rhs));
|
||||
}
|
||||
[[nodiscard]] constexpr format_flags operator | (format_flags lhs, format_flags rhs) noexcept
|
||||
{
|
||||
return static_cast<format_flags>( impl::unwrap_enum(lhs) | impl::unwrap_enum(rhs) );
|
||||
}
|
||||
}
|
||||
|
||||
namespace toml::impl
|
||||
{
|
||||
TOML_PUSH_WARNINGS
|
||||
TOML_DISABLE_ALL_WARNINGS // some compilers will complain about a tautological unsigned >= 0.
|
||||
// TINAE - char can have signed _or_ unsigned semantics and I can't
|
||||
// be arsed handling this differently
|
||||
|
||||
[[nodiscard]]
|
||||
inline toml::string_view escape_string_character(const toml::string_char& c) noexcept
|
||||
{
|
||||
if (c >= TOML_STRING_PREFIX('\x00') && c <= TOML_STRING_PREFIX('\x1F')) TOML_UNLIKELY
|
||||
return low_character_escape_table[c];
|
||||
else if (c == TOML_STRING_PREFIX('\x7F')) TOML_UNLIKELY
|
||||
return TOML_STRING_PREFIX("\\u007F"sv);
|
||||
else if (c == TOML_STRING_PREFIX('"')) TOML_UNLIKELY
|
||||
return TOML_STRING_PREFIX("\\\""sv);
|
||||
else
|
||||
return toml::string_view{ &c, 1_sz };
|
||||
}
|
||||
|
||||
TOML_POP_WARNINGS
|
||||
|
||||
struct formatter_options final
|
||||
{
|
||||
toml::string_view indent_string;
|
||||
bool quote_dates_and_times;
|
||||
};
|
||||
|
||||
template <typename CHAR = char>
|
||||
class formatter
|
||||
{
|
||||
private:
|
||||
const toml::table& source_;
|
||||
std::basic_ostream<CHAR>* stream_ = nullptr;
|
||||
formatter_options options_;
|
||||
const toml::node* source_;
|
||||
format_flags flags_;
|
||||
int indent_;
|
||||
bool naked_newline_;
|
||||
size_t indent_columns_;
|
||||
std::basic_ostream<CHAR>* stream_ = nullptr;
|
||||
|
||||
protected:
|
||||
|
||||
[[nodiscard]] const toml::table& source() const noexcept { return source_; }
|
||||
[[nodiscard]] const formatter_options& options() const noexcept { return options_; }
|
||||
[[nodiscard]] const toml::node& source() const noexcept { return *source_; }
|
||||
[[nodiscard]] format_flags flags() const noexcept { return flags_; }
|
||||
[[nodiscard]] std::basic_ostream<CHAR>& stream() const noexcept { return *stream_; }
|
||||
|
||||
static constexpr size_t indent_columns = 4;
|
||||
static constexpr toml::string_view indent_string = TOML_STRING_PREFIX(" "sv);
|
||||
[[nodiscard]] int indent() const noexcept { return indent_; }
|
||||
[[nodiscard]] size_t indent_columns() const noexcept { return indent_columns_; }
|
||||
void indent(int level) noexcept { indent_ = level; }
|
||||
void increase_indent() noexcept { indent_++; }
|
||||
void decrease_indent() noexcept { indent_--; }
|
||||
@@ -79,7 +71,7 @@ namespace toml::impl
|
||||
{
|
||||
for (int i = 0; i < indent_; i++)
|
||||
{
|
||||
print_to_stream(options_.indent_string, *stream_);
|
||||
print_to_stream(indent_string, *stream_);
|
||||
naked_newline_ = false;
|
||||
}
|
||||
}
|
||||
@@ -91,8 +83,7 @@ namespace toml::impl
|
||||
else
|
||||
{
|
||||
print_to_stream('"', *stream_);
|
||||
for (auto c : str)
|
||||
print_to_stream(escape_string_character(c), *stream_);
|
||||
print_to_stream_with_escapes(str, *stream_);
|
||||
print_to_stream('"', *stream_);
|
||||
}
|
||||
naked_newline_ = false;
|
||||
@@ -114,7 +105,7 @@ namespace toml::impl
|
||||
|
||||
if constexpr (is_date_time)
|
||||
{
|
||||
if (options_.quote_dates_and_times)
|
||||
if ((flags_ & format_flags::quote_dates_and_times) != format_flags::none)
|
||||
print_to_stream('"', *stream_);
|
||||
}
|
||||
|
||||
@@ -122,7 +113,7 @@ namespace toml::impl
|
||||
|
||||
if constexpr (is_date_time)
|
||||
{
|
||||
if (options_.quote_dates_and_times)
|
||||
if ((flags_ & format_flags::quote_dates_and_times) != format_flags::none)
|
||||
print_to_stream('"', *stream_);
|
||||
}
|
||||
|
||||
@@ -145,22 +136,9 @@ namespace toml::impl
|
||||
}
|
||||
}
|
||||
|
||||
formatter(const toml::table& source, formatter_options&& options) noexcept
|
||||
: source_{ source },
|
||||
options_{ std::move(options) }
|
||||
|
||||
{
|
||||
if (options_.indent_string.empty())
|
||||
{
|
||||
options_.indent_string = TOML_STRING_PREFIX(" "sv);
|
||||
indent_columns_ = 4_sz;
|
||||
}
|
||||
else
|
||||
{
|
||||
indent_columns_ = {};
|
||||
for (auto c : options_.indent_string)
|
||||
indent_columns_ += c == '\t' ? 4_sz : 1_sz;
|
||||
}
|
||||
}
|
||||
formatter(const toml::node& source, format_flags flags) noexcept
|
||||
: source_{ &source },
|
||||
flags_{ flags }
|
||||
{}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -45,17 +45,23 @@ namespace toml
|
||||
base::clear_naked_newline();
|
||||
}
|
||||
|
||||
void print() TOML_MAY_THROW
|
||||
{
|
||||
switch (auto source_type = base::source().type())
|
||||
{
|
||||
case node_type::table: print(*reinterpret_cast<const table*>(&base::source())); break;
|
||||
case node_type::array: print(*reinterpret_cast<const array*>(&base::source())); break;
|
||||
default: base::print(base::source(), source_type);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
TOML_NODISCARD_CTOR
|
||||
json_formatter(const toml::table& source_, toml::string_view indent_string = {}) noexcept
|
||||
: base{
|
||||
source_,
|
||||
impl::formatter_options{
|
||||
indent_string,
|
||||
true //quote_dates_and_times
|
||||
}
|
||||
}
|
||||
explicit json_formatter(
|
||||
const toml::node& source,
|
||||
format_flags flags = format_flags::quote_dates_and_times) noexcept
|
||||
: base{ source, flags }
|
||||
{}
|
||||
|
||||
template <typename T>
|
||||
@@ -63,7 +69,7 @@ namespace toml
|
||||
TOML_MAY_THROW
|
||||
{
|
||||
rhs.attach(lhs);
|
||||
rhs.print(rhs.source());
|
||||
rhs.print();
|
||||
rhs.detach();
|
||||
return lhs;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
|
||||
namespace toml
|
||||
{
|
||||
/// \brief A TOML node.
|
||||
///
|
||||
/// \detail A parsed TOML document forms a tree made up of tables, arrays and values.
|
||||
/// This type is the base of each of those, providing a lot of the polymorphic plumbing.
|
||||
class TOML_INTERFACE node
|
||||
{
|
||||
private:
|
||||
@@ -43,25 +47,46 @@ namespace toml
|
||||
|
||||
virtual ~node() noexcept = default;
|
||||
|
||||
|
||||
/// \brief Returns the node's type identifier.
|
||||
[[nodiscard]] virtual node_type type() const noexcept = 0;
|
||||
|
||||
|
||||
/// \brief Returns true if this node is a table.
|
||||
[[nodiscard]] virtual bool is_table() const noexcept = 0;
|
||||
/// \brief Returns true if this node is an array.
|
||||
[[nodiscard]] virtual bool is_array() const noexcept = 0;
|
||||
/// \brief Returns true if this node is a value.
|
||||
[[nodiscard]] virtual bool is_value() const noexcept = 0;
|
||||
|
||||
/// \brief Returns true if this node is a string value.
|
||||
[[nodiscard]] virtual bool is_string() const noexcept { return false; }
|
||||
/// \brief Returns true if this node is an integer value.
|
||||
[[nodiscard]] virtual bool is_integer() const noexcept { return false; }
|
||||
/// \brief Returns true if this node is an floating-point value.
|
||||
[[nodiscard]] virtual bool is_floating_point() const noexcept { return false; }
|
||||
/// \brief Returns true if this node is a boolean value.
|
||||
[[nodiscard]] virtual bool is_boolean() const noexcept { return false; }
|
||||
/// \brief Returns true if this node is a local date value.
|
||||
[[nodiscard]] virtual bool is_date() const noexcept { return false; }
|
||||
/// \brief Returns true if this node is a local time value.
|
||||
[[nodiscard]] virtual bool is_time() const noexcept { return false; }
|
||||
/// \brief Returns true if this node is a date-time value.
|
||||
[[nodiscard]] virtual bool is_date_time() const noexcept { return false; }
|
||||
/// \brief Returns true if this node is an array containing only tables.
|
||||
[[nodiscard]] virtual bool is_array_of_tables() const noexcept { return false; }
|
||||
|
||||
|
||||
/// \brief Checks if a node is a specific type.
|
||||
///
|
||||
/// \tparam T The
|
||||
///
|
||||
/// \returns Returns true if this node is an instance
|
||||
template <typename T>
|
||||
[[nodiscard]] bool is() const noexcept
|
||||
[[nodiscard]] TOML_ALWAYS_INLINE
|
||||
bool is() const noexcept
|
||||
{
|
||||
using type = value_of<T>;
|
||||
using type = value_of<impl::remove_cvref_t<T>>;
|
||||
static_assert(
|
||||
impl::is_value_or_node<type>,
|
||||
"Template type parameter must be one of the basic value types, a toml::table, or a toml::array"
|
||||
@@ -155,8 +180,7 @@ namespace toml
|
||||
static decltype(auto) do_visit(N* node, FUNC&& visitor) TOML_MAY_THROW
|
||||
{
|
||||
static_assert(
|
||||
impl::is_generic_invocable_v<FUNC&&>
|
||||
|| std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<table>())>
|
||||
std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<table>())>
|
||||
|| std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<array>())>
|
||||
|| std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<string>())>
|
||||
|| std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<int64_t>())>
|
||||
@@ -168,7 +192,7 @@ namespace toml
|
||||
"Visitors must be invocable for at least one of the toml::node specializations"
|
||||
);
|
||||
|
||||
static constexpr auto is_exhaustive = impl::is_generic_invocable_v<FUNC&&> || (
|
||||
static constexpr auto is_exhaustive =
|
||||
std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<table>())>
|
||||
&& std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<array>())>
|
||||
&& std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<string>())>
|
||||
@@ -177,54 +201,44 @@ namespace toml
|
||||
&& std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<bool>())>
|
||||
&& std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<date>())>
|
||||
&& std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<time>())>
|
||||
&& std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<date_time>())>
|
||||
);
|
||||
&& std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<date_time>())>;
|
||||
|
||||
switch (node->type())
|
||||
{
|
||||
case node_type::table:
|
||||
if constexpr (impl::is_generic_invocable_v<FUNC&&>
|
||||
|| std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<table>())>)
|
||||
if constexpr (std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<table>())>)
|
||||
return std::forward<FUNC>(visitor)(*node->template reinterpret_as<table>());
|
||||
break;
|
||||
case node_type::array:
|
||||
if constexpr (impl::is_generic_invocable_v<FUNC&&>
|
||||
|| std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<array>())>)
|
||||
if constexpr (std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<array>())>)
|
||||
return std::forward<FUNC>(visitor)(*node->template reinterpret_as<array>());
|
||||
break;
|
||||
case node_type::string:
|
||||
if constexpr (impl::is_generic_invocable_v<FUNC&&>
|
||||
|| std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<string>())>)
|
||||
if constexpr (std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<string>())>)
|
||||
return std::forward<FUNC>(visitor)(*node->template reinterpret_as<string>());
|
||||
break;
|
||||
case node_type::integer:
|
||||
if constexpr (impl::is_generic_invocable_v<FUNC&&>
|
||||
|| std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<int64_t>())>)
|
||||
if constexpr (std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<int64_t>())>)
|
||||
return std::forward<FUNC>(visitor)(*node->template reinterpret_as<int64_t>());
|
||||
break;
|
||||
case node_type::floating_point:
|
||||
if constexpr (impl::is_generic_invocable_v<FUNC&&>
|
||||
|| std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<double>())>)
|
||||
if constexpr (std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<double>())>)
|
||||
return std::forward<FUNC>(visitor)(*node->template reinterpret_as<double>());
|
||||
break;
|
||||
case node_type::boolean:
|
||||
if constexpr (impl::is_generic_invocable_v<FUNC&&>
|
||||
|| std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<bool>())>)
|
||||
if constexpr (std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<bool>())>)
|
||||
return std::forward<FUNC>(visitor)(*node->template reinterpret_as<bool>());
|
||||
break;
|
||||
case node_type::date:
|
||||
if constexpr (impl::is_generic_invocable_v<FUNC&&>
|
||||
|| std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<date>())>)
|
||||
if constexpr (std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<date>())>)
|
||||
return std::forward<FUNC>(visitor)(*node->template reinterpret_as<date>());
|
||||
break;
|
||||
case node_type::time:
|
||||
if constexpr (impl::is_generic_invocable_v<FUNC&&>
|
||||
|| std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<time>())>)
|
||||
if constexpr (std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<time>())>)
|
||||
return std::forward<FUNC>(visitor)(*node->template reinterpret_as<time>());
|
||||
break;
|
||||
case node_type::date_time:
|
||||
if constexpr (impl::is_generic_invocable_v<FUNC&&>
|
||||
|| std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<date_time>())>)
|
||||
if constexpr (std::is_invocable_v<FUNC&&, decltype(*node->template reinterpret_as<date_time>())>)
|
||||
return std::forward<FUNC>(visitor)(*node->template reinterpret_as<date_time>());
|
||||
break;
|
||||
TOML_NO_DEFAULT_CASE;
|
||||
|
||||
@@ -252,4 +252,26 @@ namespace toml::impl
|
||||
if (val.time_offset)
|
||||
print_to_stream(*val.time_offset, stream);
|
||||
}
|
||||
|
||||
TOML_PUSH_WARNINGS
|
||||
TOML_DISABLE_ALL_WARNINGS
|
||||
|
||||
template <typename T, typename CHAR>
|
||||
void print_to_stream_with_escapes(T && str, std::basic_ostream<CHAR>& stream) TOML_MAY_THROW
|
||||
{
|
||||
static_assert(sizeof(CHAR) == 1);
|
||||
for (auto c : str)
|
||||
{
|
||||
if (c >= TOML_STRING_PREFIX('\x00') && c <= TOML_STRING_PREFIX('\x1F')) TOML_UNLIKELY
|
||||
print_to_stream(low_character_escape_table[c], stream);
|
||||
else if (c == TOML_STRING_PREFIX('\x7F')) TOML_UNLIKELY
|
||||
print_to_stream(TOML_STRING_PREFIX("\\u007F"sv), stream);
|
||||
else if (c == TOML_STRING_PREFIX('"')) TOML_UNLIKELY
|
||||
print_to_stream(TOML_STRING_PREFIX("\\\""sv), stream);
|
||||
else
|
||||
print_to_stream(c, stream);
|
||||
}
|
||||
}
|
||||
|
||||
TOML_POP_WARNINGS
|
||||
}
|
||||
|
||||
@@ -224,5 +224,8 @@ namespace toml
|
||||
|
||||
[[nodiscard]] inline node_view<table> operator[] (string_view) noexcept;
|
||||
[[nodiscard]] inline node_view<const table> operator[] (string_view) const noexcept;
|
||||
|
||||
template <typename CHAR>
|
||||
friend inline std::basic_ostream<CHAR>& operator << (std::basic_ostream<CHAR>&, const table&) TOML_MAY_THROW;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "toml_node.h"
|
||||
#include "toml_print_to_stream.h"
|
||||
|
||||
namespace toml
|
||||
{
|
||||
@@ -102,7 +103,18 @@ namespace toml
|
||||
template <typename CHAR>
|
||||
friend std::basic_ostream<CHAR>& operator << (std::basic_ostream<CHAR>& lhs, const value& rhs) TOML_MAY_THROW
|
||||
{
|
||||
impl::print_to_stream(rhs.val_, lhs);
|
||||
// this is the same behaviour as default_formatter, but it's so simple that there's
|
||||
// no need to spin up a new instance of it just for individual values.
|
||||
|
||||
if constexpr (std::is_same_v<T, string>)
|
||||
{
|
||||
impl::print_to_stream('"', lhs);
|
||||
impl::print_to_stream_with_escapes(rhs.val_, lhs);
|
||||
impl::print_to_stream('"', lhs);
|
||||
}
|
||||
else
|
||||
impl::print_to_stream(rhs.val_, lhs);
|
||||
|
||||
return lhs;
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user