Skip to content

Commit

Permalink
Initial TOML write support (#1603)
Browse files Browse the repository at this point in the history
* Adding TOML format

* Initial TOML writing

* More tests

* Remove duplicates

* Added to.hpp to remove duplicate code

* Removing duplicate filesystem_path logic

* Fix nested TOML output

* More TOML write tests

* Update toml_test.cpp
  • Loading branch information
stephenberry authored Feb 11, 2025
1 parent fa4baa5 commit b45d81d
Show file tree
Hide file tree
Showing 14 changed files with 908 additions and 120 deletions.
21 changes: 1 addition & 20 deletions include/glaze/beve/write.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "glaze/beve/header.hpp"
#include "glaze/core/opts.hpp"
#include "glaze/core/reflect.hpp"
#include "glaze/core/to.hpp"
#include "glaze/core/seek.hpp"
#include "glaze/core/write.hpp"
#include "glaze/util/dump.hpp"
Expand Down Expand Up @@ -127,16 +128,6 @@ namespace glz
}
};

template <>
struct to<BEVE, hidden>
{
template <auto Opts>
GLZ_ALWAYS_INLINE static void op(auto&& value, auto&&, auto&&...)
{
static_assert(false_v<decltype(value)>, "hidden type should not be written");
}
};

template <is_bitset T>
struct to<BEVE, T>
{
Expand Down Expand Up @@ -811,16 +802,6 @@ namespace glz
}
};

template <filesystem_path T>
struct to<BEVE, T>
{
template <auto Opts, class... Args>
GLZ_ALWAYS_INLINE static void op(auto&& value, Args&&... args)
{
to<BEVE, decltype(value.string())>::template op<Opts>(value.string(), std::forward<Args>(args)...);
}
};

template <auto& Partial, auto Opts, class T, class Ctx, class B, class IX>
concept write_beve_partial_invocable = requires(T&& value, Ctx&& ctx, B&& b, IX&& ix) {
to_partial<BEVE, std::remove_cvref_t<T>>::template op<Partial, Opts>(
Expand Down
47 changes: 47 additions & 0 deletions include/glaze/concepts/container_concepts.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -342,3 +342,50 @@ namespace glz
template <class T>
constexpr bool const_value_v = std::is_const_v<std::remove_pointer_t<std::remove_reference_t<T>>>;
}

namespace glz::detail
{
template <class Container>
using iterator_pair_type =
typename std::iterator_traits<decltype(std::begin(std::declval<Container&>()))>::value_type;

template <class Container, typename Iterator = iterator_pair_type<Container>>
struct iterator_second_impl;

template <class Container, typename Iterator>
requires has_value_type<Iterator>
struct iterator_second_impl<Container, Iterator>
{
using type = typename Iterator::value_type;
};

template <class Container, typename Iterator>
requires(!has_value_type<Iterator> && has_second_type<Iterator>)
struct iterator_second_impl<Container, Iterator>
{
using type = typename Iterator::second_type;
};

template <class Container>
using iterator_second_type = typename iterator_second_impl<Container>::type;

template <class Container, typename Iterator = iterator_pair_type<Container>>
struct iterator_first_impl;

template <class Container, typename Iterator>
requires has_value_type<Iterator>
struct iterator_first_impl<Container, Iterator>
{
using type = typename Iterator::value_type;
};

template <class Container, typename Iterator>
requires(!has_value_type<Iterator> && has_first_type<Iterator>)
struct iterator_first_impl<Container, Iterator>
{
using type = typename Iterator::first_type;
};

template <class Container>
using iterator_first_type = typename iterator_first_impl<Container>::type;
}
3 changes: 3 additions & 0 deletions include/glaze/core/common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,9 @@ namespace glz
*t
};
};

template <class T>
concept nullable_like = nullable_t<T> && (!is_expected<T> && !std::is_array_v<T>);

// For optional like types that cannot overload `operator bool()`
template <class T>
Expand Down
21 changes: 21 additions & 0 deletions include/glaze/core/opts.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ namespace glz
inline constexpr uint32_t JSON = 10;
inline constexpr uint32_t JSON_PTR = 20;
inline constexpr uint32_t NDJSON = 100; // new line delimited JSON
inline constexpr uint32_t TOML = 400;
inline constexpr uint32_t STENCIL = 500;
inline constexpr uint32_t CSV = 10000;

Expand Down Expand Up @@ -313,6 +314,14 @@ namespace glz
ret.format = JSON;
return ret;
}

template <opts Opts>
constexpr auto set_toml()
{
opts ret = Opts;
ret.format = TOML;
return ret;
}
}

namespace glz
Expand Down Expand Up @@ -350,6 +359,12 @@ namespace glz
template <class T>
concept read_ndjson_supported = requires { detail::from<NDJSON, std::remove_cvref_t<T>>{}; };

template <class T>
concept write_toml_supported = requires { detail::to<TOML, std::remove_cvref_t<T>>{}; };

template <class T>
concept read_toml_supported = requires { detail::from<TOML, std::remove_cvref_t<T>>{}; };

template <class T>
concept write_csv_supported = requires { detail::to<CSV, std::remove_cvref_t<T>>{}; };

Expand All @@ -368,6 +383,9 @@ namespace glz
else if constexpr (Format == NDJSON) {
return write_ndjson_supported<T>;
}
else if constexpr (Format == TOML) {
return write_toml_supported<T>;
}
else if constexpr (Format == CSV) {
return write_csv_supported<T>;
}
Expand All @@ -388,6 +406,9 @@ namespace glz
else if constexpr (Format == NDJSON) {
return read_ndjson_supported<T>;
}
else if constexpr (Format == TOML) {
return read_toml_supported<T>;
}
else if constexpr (Format == CSV) {
return read_csv_supported<T>;
}
Expand Down
17 changes: 17 additions & 0 deletions include/glaze/core/reflect.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1951,6 +1951,23 @@ namespace glz
}
}

namespace glz::detail
{
template <class T>
inline constexpr size_t maximum_key_size = [] {
constexpr auto N = reflect<T>::size;
size_t maximum{};
for (size_t i = 0; i < N; ++i) {
if (reflect<T>::keys[i].size() > maximum) {
maximum = reflect<T>::keys[i].size();
}
}
return maximum + 2; // add quotes for JSON
}();

inline constexpr uint64_t round_up_to_nearest_16(const uint64_t value) noexcept { return (value + 15) & ~15ull; }
}

namespace glz
{
// The Callable comes second as ranges::for_each puts the callable at the end
Expand Down
42 changes: 42 additions & 0 deletions include/glaze/core/to.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Glaze Library
// For the license information refer to glaze.hpp

#pragma once

#include "glaze/core/common.hpp"
#include "glaze/core/opts.hpp"

// Common behavior for `to` specializations, typically applies for all formats

namespace glz::detail
{
template <uint32_t Format>
struct to<Format, hidden>
{
template <auto Opts>
static void op(auto&& value, auto&&...) noexcept
{
static_assert(false_v<decltype(value)>, "hidden type should not be written");
}
};

template <uint32_t Format>
struct to<Format, skip>
{
template <auto Opts>
static void op(auto&& value, auto&&...) noexcept
{
static_assert(false_v<decltype(value)>, "skip type should not be written");
}
};

template <uint32_t Format, filesystem_path T>
struct to<Format, T>
{
template <auto Opts, class... Args>
static void op(auto&& value, is_context auto&& ctx, Args&&... args)
{
to<Format, decltype(value.string())>::template op<Opts>(value.string(), ctx, std::forward<Args>(args)...);
}
};
}
9 changes: 9 additions & 0 deletions include/glaze/core/wrappers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@

namespace glz
{
// treat a value as quoted to avoid double parsing into a value
template <class T>
struct quoted_t
{
static constexpr bool glaze_wrapper = true;
using value_type = T;
T& val;
};

namespace detail
{
template <class T, auto OptsMemPtr>
Expand Down
9 changes: 0 additions & 9 deletions include/glaze/json/wrappers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,6 @@

namespace glz
{
// treat a value as quoted to avoid double parsing into a value
template <class T>
struct quoted_t
{
static constexpr bool glaze_wrapper = true;
using value_type = T;
T& val;
};

namespace detail
{
template <class T>
Expand Down
92 changes: 1 addition & 91 deletions include/glaze/json/write.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "glaze/core/opts.hpp"
#include "glaze/core/reflect.hpp"
#include "glaze/core/write.hpp"
#include "glaze/core/to.hpp"
#include "glaze/core/write_chars.hpp"
#include "glaze/json/ptr.hpp"
#include "glaze/util/dump.hpp"
Expand Down Expand Up @@ -62,9 +63,6 @@ namespace glz
}
};

template <class T>
concept nullable_like = nullable_t<T> && (!is_expected<T> && !std::is_array_v<T>);

// Returns 0 if we cannot determine the required padding,
// in which case the `to` specialization must allocate buffer space
// Some types like numbers must have space to be quoted
Expand Down Expand Up @@ -204,26 +202,6 @@ namespace glz
}
};

template <>
struct to<JSON, hidden>
{
template <auto Opts>
static void op(auto&& value, auto&&...) noexcept
{
static_assert(false_v<decltype(value)>, "hidden type should not be written");
}
};

template <>
struct to<JSON, skip>
{
template <auto Opts>
static void op(auto&& value, auto&&...) noexcept
{
static_assert(false_v<decltype(value)>, "skip type should not be written");
}
};

template <is_member_function_pointer T>
struct to<JSON, T>
{
Expand Down Expand Up @@ -646,16 +624,6 @@ namespace glz
}
};

template <filesystem_path T>
struct to<JSON, T>
{
template <auto Opts, class... Args>
static void op(auto&& value, is_context auto&& ctx, Args&&... args)
{
to<JSON, decltype(value.string())>::template op<Opts>(value.string(), ctx, std::forward<Args>(args)...);
}
};

template <class T>
requires((glaze_enum_t<T> || (meta_keys<T> && std::is_enum_v<std::decay_t<T>>)) && not custom_write<T>)
struct to<JSON, T>
Expand Down Expand Up @@ -854,50 +822,6 @@ namespace glz
concept array_padding_known =
requires { typename T::value_type; } && (required_padding<typename T::value_type>() > 0);

template <class Container>
using iterator_pair_type =
typename std::iterator_traits<decltype(std::begin(std::declval<Container&>()))>::value_type;

template <class Container, typename Iterator = iterator_pair_type<Container>>
struct iterator_second_impl;

template <class Container, typename Iterator>
requires has_value_type<Iterator>
struct iterator_second_impl<Container, Iterator>
{
using type = typename Iterator::value_type;
};

template <class Container, typename Iterator>
requires(!has_value_type<Iterator> && has_second_type<Iterator>)
struct iterator_second_impl<Container, Iterator>
{
using type = typename Iterator::second_type;
};

template <class Container>
using iterator_second_type = typename iterator_second_impl<Container>::type;

template <class Container, typename Iterator = iterator_pair_type<Container>>
struct iterator_first_impl;

template <class Container, typename Iterator>
requires has_value_type<Iterator>
struct iterator_first_impl<Container, Iterator>
{
using type = typename Iterator::value_type;
};

template <class Container, typename Iterator>
requires(!has_value_type<Iterator> && has_first_type<Iterator>)
struct iterator_first_impl<Container, Iterator>
{
using type = typename Iterator::first_type;
};

template <class Container>
using iterator_first_type = typename iterator_first_impl<Container>::type;

template <class T>
requires(writable_array_t<T> || writable_map_t<T>)
struct to<JSON, T>
Expand Down Expand Up @@ -1616,20 +1540,6 @@ namespace glz
}
};

template <class T>
inline constexpr size_t maximum_key_size = [] {
constexpr auto N = reflect<T>::size;
size_t maximum{};
for (size_t i = 0; i < N; ++i) {
if (reflect<T>::keys[i].size() > maximum) {
maximum = reflect<T>::keys[i].size();
}
}
return maximum + 2; // add quotes
}();

inline constexpr uint64_t round_up_to_nearest_16(const uint64_t value) noexcept { return (value + 15) & ~15ull; }

// Only use this if you are not prettifying
// Returns zero if the fixed size cannot be determined
template <class T>
Expand Down
6 changes: 6 additions & 0 deletions include/glaze/toml.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Glaze Library
// For the license information refer to glaze.hpp

#pragma once

#include "glaze/toml/write.hpp"
Loading

0 comments on commit b45d81d

Please sign in to comment.