Documentation generated using m.css
+##! M_HTML_HEADER =
diff --git a/docs/tomlplusplus.css b/docs/tomlplusplus.css
index 3bf6971..806982d 100644
--- a/docs/tomlplusplus.css
+++ b/docs/tomlplusplus.css
@@ -1,3 +1,4 @@
+/*
table.m-table th
{
color: #ffe698;
@@ -49,71 +50,16 @@ dl.m-doc dd
margin-bottom: 0.8rem;
}
-a.tpp-external
-{
- font-weight: normal;
-}
-
.m-doc-template a, dl.m-doc dd a, ul.m-doc li > span.m-doc a
{
- color: #858585;
+ color: #858585;
}
.m-doc-template a.tpp-external:hover,
dl.m-doc dd a.tpp-external:hover,
ul.m-doc li > span.m-doc a.tpp-external:hover
{
- color: inherit;
-}
-
-
-.tpp-swatch, .tpp-enable-if > *
-{
- display: inline-block;
- border-radius: 0.2rem;
-}
-
-.tpp-enable-if
-{
- margin-bottom: 2px;
-}
-
-.tpp-enable-if > *
-{
- background-clip: padding-box !important;
- padding: 0px 2px;
- text-decoration: none;
-}
-
-.tpp-enable-if > a
-{
- white-space: nowrap;
- font-size: 0.8rem;
- font-weight: bold;
- background-color: #858585;
- color: #050505;
- padding-bottom: 0px;
- margin: 0px 1px;
- margin-bottom: 2px;
-}
-
-.tpp-enable-if > a:hover
-{
- background-color: #747474;
- color: initial;
-}
-
-.tpp-enable-if > span
-{
- display: none;
- padding-left: 2em;
-
-}
-
-.tpp-swatch
-{
- min-width: 3em;
- min-height: 1em;
+ color: inherit;
}
body > header > nav
@@ -126,12 +72,12 @@ html
background-color: #2d2d30;
}
-pre, code, .m-label/*.m-flat.m-success*/, .tpp-enable-if > a
+pre, code, .m-label, .tpp-enable-if > a
{
font-family: 'Consolas', monospace;
}
-pre, .m-doc-search-content /*, .tpp-enable-if > full*/
+pre, .m-doc-search-content
{
background-color: #1e1e1e;
}
@@ -141,31 +87,6 @@ pre.m-code, code
background-color: #1e1e1e88;
}
-.m-code .c1 /* comments */
-{
- color: rgb(87,166,74);
-}
-
-.m-code .mi, .m-code .mf /* literals */
-{
- color: rgb(181,206,168);
-}
-
-.m-code .k /* keywords */
-{
- color: rgb(86,156,214);
-}
-
-.m-code .n /* names */
-{
- color: rgb(220,220,220);
-}
-
-.m-code .p /* punctuation */
-{
- color: rgb(120,120,120);
-}
-
article section.m-doc-details > div > h3:first-child, article section > h2, body > footer > nav
{
background-color: #252526;
@@ -178,21 +99,7 @@ article section.m-doc-details > div > h3:first-child, article section > h2, body
.m-label:not(.m-flat)
{
- font-weight: bold;
-}
-
-header
-{
- position: fixed;
- left: 0;
- right: 0;
- top: 0;
- z-index: 100;
-}
-
-body
-{
- margin-top: 3rem;
+ font-weight: bold;
}
div.m-doc-include
@@ -210,9 +117,91 @@ div.m-doc-include
color: #9999AA;
}
-article, article > header, article section
+*/
+
+html, body
{
- margin-bottom: 3em;
- background-color: red;
- background-color: inherit;
+ scroll-padding-top: 3.5rem;
+}
+
+body
+{
+ margin-top: 3rem;
+}
+
+header {
+ position: fixed;
+ left: 0;
+ right: 0;
+ top: 0;
+ z-index: 100;
+}
+
+article, article > header, article section {
+ margin-bottom: 3em;
+}
+
+pre, code, .tpp-enable-if > a {
+ font-family: 'Consolas', 'Source Sans Pro', monospace;
+}
+
+a.tpp-external {
+ font-weight: normal;
+}
+
+.tpp-enable-if {
+ margin-bottom: 2px;
+}
+
+.tpp-enable-if > * {
+ display: inline-block;
+ border-radius: 0.2rem;
+ background-clip: padding-box !important;
+ padding: 0px 2px;
+ text-decoration: none;
+}
+
+.tpp-enable-if > a {
+ white-space: nowrap;
+ font-size: 0.8rem;
+ font-weight: bold;
+ background-color: #858585;
+ color: #050505;
+ padding-bottom: 0px;
+ margin: 0px 1px;
+ margin-bottom: 2px;
+}
+
+ .tpp-enable-if > a:hover {
+ background-color: #747474;
+ color: initial;
+ }
+
+.tpp-enable-if > span {
+ display: none;
+ padding-left: 2em;
+}
+
+nav .m-thin {
+ margin-left: 0.5em;
+}
+
+.m-code .c1 {
+ color: rgb(87,166,74);
+}
+
+.m-code .mi, .m-code .mf {
+ color: rgb(181,206,168);
+}
+
+.m-code .k {
+ color: rgb(86,156,214);
+}
+
+.m-code .n {
+ color: rgb(220,220,220);
+}
+
+.m-code .p {
+ color: rgb(120,120,120);
}
diff --git a/examples/parse_file.cpp b/examples/parse_file.cpp
index 2c8a436..e7df419 100644
--- a/examples/parse_file.cpp
+++ b/examples/parse_file.cpp
@@ -22,8 +22,6 @@ int main(int argc, char** argv)
}
try
{
- //const auto table = toml::parse(file); //works but parse_errors would not include the source path
-
const auto table = toml::parse(file, std::move(path));
std::cout << table << std::endl;
diff --git a/include/toml++/toml.h b/include/toml++/toml.h
index 10924f2..6a79606 100644
--- a/include/toml++/toml.h
+++ b/include/toml++/toml.h
@@ -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
diff --git a/include/toml++/toml_array.h b/include/toml++/toml_array.h
index d7579b7..d8c6db8 100644
--- a/include/toml++/toml_array.h
+++ b/include/toml++/toml_array.h
@@ -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("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;
using const_iterator = impl::array_iterator;
@@ -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
[[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();
+ }
+ /// \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
[[nodiscard]] node_of* get_as(size_t index) noexcept
{
return values[index]->as();
}
+ /// \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
[[nodiscard]] const node_of* get_as(size_t index) const noexcept
{
return values[index]->as();
}
- [[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
+ friend inline std::basic_ostream& operator << (std::basic_ostream&, const array&) TOML_MAY_THROW;
};
}
diff --git a/include/toml++/toml_common.h b/include/toml++/toml_common.h
index 0f73017..7d2ccdf 100644
--- a/include/toml++/toml_common.h
+++ b/include/toml++/toml_common.h
@@ -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(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;
/// \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
+ [[nodiscard]] TOML_ALWAYS_INLINE
+ constexpr std::underlying_type_t unwrap_enum(T val) noexcept
+ {
+ return static_cast>(val);
+ }
+
// Q: "why not use std::find??"
// A: Because 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
- struct is_generic_invocable
- {
- template
- static constexpr auto test(U&&) -> decltype(std::declval()(std::declval()), std::true_type{});
- static constexpr std::false_type test(...);
-
- struct tester {};
- static constexpr auto value = decltype(test(tester{}))::value;
- };
-
- template
- inline constexpr bool is_generic_invocable_v = is_generic_invocable::value;
-
class parser;
template
@@ -692,12 +682,17 @@ namespace toml::impl
namespace toml
{
+
+ /// \brief Helper alias that wraps a type up as it's TOML node equivalent.
template
using node_of = typename impl::node_wrapper::type;
+ /// \brief Helper alias that unwraps TOML node type to it's raw value equivalent.
template
using value_of = typename impl::node_unwrapper::type;
+
+ /// \brief Pretty-prints the value of a node_type to a stream.
template
inline std::basic_ostream& operator << (std::basic_ostream& lhs, node_type rhs) TOML_MAY_THROW
{
diff --git a/include/toml++/toml_default_formatter.h b/include/toml++/toml_default_formatter.h
index 9147920..a917caa 100644
--- a/include/toml++/toml_default_formatter.h
+++ b/include/toml++/toml_default_formatter.h
@@ -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(original_indent < 0 ? 0 : original_indent)
+ base::indent_columns * static_cast(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(&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(&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
@@ -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{ rhs };
}
+
+ template
+ inline std::basic_ostream& operator << (std::basic_ostream& lhs, const array& rhs) TOML_MAY_THROW
+ {
+ return lhs << default_formatter{ rhs };
+ }
}
diff --git a/include/toml++/toml_formatter.h b/include/toml++/toml_formatter.h
index 89cf54e..6ffebec 100644
--- a/include/toml++/toml_formatter.h
+++ b/include/toml++/toml_formatter.h
@@ -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(impl::unwrap_enum(lhs) & impl::unwrap_enum(rhs));
+ }
+ [[nodiscard]] constexpr format_flags operator | (format_flags lhs, format_flags rhs) noexcept
+ {
+ return static_cast( 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
class formatter
{
private:
- const toml::table& source_;
- std::basic_ostream* stream_ = nullptr;
- formatter_options options_;
+ const toml::node* source_;
+ format_flags flags_;
int indent_;
bool naked_newline_;
- size_t indent_columns_;
+ std::basic_ostream* 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& 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 }
+ {}
};
}
diff --git a/include/toml++/toml_json_formatter.h b/include/toml++/toml_json_formatter.h
index a1f927c..94cebae 100644
--- a/include/toml++/toml_json_formatter.h
+++ b/include/toml++/toml_json_formatter.h
@@ -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(&base::source())); break;
+ case node_type::array: print(*reinterpret_cast(&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
@@ -63,7 +69,7 @@ namespace toml
TOML_MAY_THROW
{
rhs.attach(lhs);
- rhs.print(rhs.source());
+ rhs.print();
rhs.detach();
return lhs;
}
diff --git a/include/toml++/toml_node.h b/include/toml++/toml_node.h
index a7606a4..cb227d6 100644
--- a/include/toml++/toml_node.h
+++ b/include/toml++/toml_node.h
@@ -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
- [[nodiscard]] bool is() const noexcept
+ [[nodiscard]] TOML_ALWAYS_INLINE
+ bool is() const noexcept
{
- using type = value_of;
+ using type = value_of>;
static_assert(
impl::is_value_or_node,
"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
- || std::is_invocable_vtemplate reinterpret_as
())>
+ std::is_invocable_vtemplate reinterpret_as
())>
|| std::is_invocable_vtemplate reinterpret_as())>
|| std::is_invocable_vtemplate reinterpret_as())>
|| std::is_invocable_vtemplate reinterpret_as())>
@@ -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 || (
+ static constexpr auto is_exhaustive =
std::is_invocable_vtemplate reinterpret_as