<commit_before>#pragma once

#include <string>
#include <vector>
#include <map>

#include "rapidjson/writer.h"
#include "rapidjson/prettywriter.h"
#include "rapidjson/error/en.h"
#include "rapidjson/stringbuffer.h"

#include "acmacs-base/read-file.hh"

// ----------------------------------------------------------------------

template <typename RW> class JsonWriterT : public RW
{
 public:
    inline JsonWriterT(std::string aKeyword) : RW(mBuffer), mKeyword(aKeyword) {}
    inline operator std::string() const { return mBuffer.GetString(); }
    inline std::string keyword() const { return mKeyword; }

 private:
    rapidjson::StringBuffer mBuffer;
    std::string mKeyword;
};

// template <> inline JsonWriterT<rapidjson::PrettyWriter<rapidjson::StringBuffer>>::JsonWriterT(std::string aKeyword)
//     : rapidjson::PrettyWriter<rapidjson::StringBuffer>(mBuffer), mKeyword(aKeyword)
// {
//     SetIndent(' ', 1);
// }

typedef JsonWriterT<rapidjson::Writer<rapidjson::StringBuffer>> JsonWriter;
typedef JsonWriterT<rapidjson::PrettyWriter<rapidjson::StringBuffer>> JsonPrettyWriter;

// ----------------------------------------------------------------------

enum _StartArray { StartArray };
enum _EndArray { EndArray };
enum _StartObject { StartObject };
enum _EndObject { EndObject };

template <typename RW> inline JsonWriterT<RW>& operator <<(JsonWriterT<RW>& writer, _StartArray) { writer.StartArray(); return writer; }
template <typename RW> inline JsonWriterT<RW>& operator <<(JsonWriterT<RW>& writer, _EndArray) { writer.EndArray(); return writer; }
template <typename RW> inline JsonWriterT<RW>& operator <<(JsonWriterT<RW>& writer, _StartObject) { writer.StartObject(); return writer; }
template <typename RW> inline JsonWriterT<RW>& operator <<(JsonWriterT<RW>& writer, _EndObject) { writer.EndObject(); return writer; }

template <typename RW> inline JsonWriterT<RW>& operator <<(JsonWriterT<RW>& writer, const char* s) { writer.String(s, static_cast<unsigned>(strlen(s))); return writer; }
template <typename RW> inline JsonWriterT<RW>& operator <<(JsonWriterT<RW>& writer, std::string s) { writer.String(s.c_str(), static_cast<unsigned>(s.size())); return writer; }
template <typename RW> inline JsonWriterT<RW>& operator <<(JsonWriterT<RW>& writer, int value) { writer.Int(value); return writer; }
template <typename RW> inline JsonWriterT<RW>& operator <<(JsonWriterT<RW>& writer, double value) { writer.Double(value); return writer; }

class JsonObjectKey
{
 public:
    inline JsonObjectKey(const char* v) : value(v) {}
    inline JsonObjectKey(std::string v) : value(v.c_str()) {}
    inline operator const char*() const { return value; }

 private:
    const char* value;
};

template <typename RW> inline JsonWriterT<RW>& operator <<(JsonWriterT<RW>& writer, JsonObjectKey value) { writer.Key(value); return writer; }

// ----------------------------------------------------------------------

template <typename RW, typename T> inline JsonWriterT<RW>& operator <<(JsonWriterT<RW>& writer, const std::vector<T>& list)
{
    writer << StartArray;
    for (const auto& e: list)
        writer << e;
    return writer << EndArray;
}

// ----------------------------------------------------------------------

template <typename RW> inline JsonWriterT<RW>& operator <<(JsonWriterT<RW>& writer, const std::vector<std::vector<std::string>>& list_list_strings)
{
    writer << StartArray;
    for (const auto& e: list_list_strings)
        writer << e;
    return writer << EndArray;
}

// ----------------------------------------------------------------------

template <typename RW> inline JsonWriterT<RW>& operator <<(JsonWriterT<RW>& writer, const std::map<std::string, std::vector<std::string>>& map_list_strings)
{
    writer << StartObject;
    for (const auto& e: map_list_strings)
        writer << JsonObjectKey(e.first) << e.second;
    return writer << EndObject;
}

// ----------------------------------------------------------------------

template <typename Key, typename Value> class _if_not_empty
{
 public:
    inline _if_not_empty(Key&& key, Value&& value) : mKey(key), mValue(value) {}

    template <typename RW> friend inline JsonWriterT<RW>& operator <<(JsonWriterT<RW>& writer, const _if_not_empty<Key, Value>& data)
        {
            if (!data.mValue.empty())
                writer << data.mKey << data.mValue;
            return writer;
        }

 private:
    Key mKey;
    Value mValue;
};

template <typename Key, typename Value> inline auto if_not_empty(Key&& key, Value&& value) { return _if_not_empty<Key, Value>(std::forward<Key>(key), std::forward<Value>(value)); }

// ----------------------------------------------------------------------
// ----------------------------------------------------------------------

template <typename V> inline std::string pretty_json(const V& value, std::string keyword, size_t indent, bool insert_emacs_indent_hint)
{
    JsonPrettyWriter writer(keyword);
    writer.SetIndent(' ', static_cast<unsigned int>(indent));
    writer << value;
    std::string result = writer;<commit_msg>operator <<(JsonWriterT<RW>& writer, Key key)<commit_after>#pragma once

#include <string>
#include <vector>
#include <map>

#include "rapidjson/writer.h"
#include "rapidjson/prettywriter.h"
#include "rapidjson/error/en.h"
#include "rapidjson/stringbuffer.h"

#include "acmacs-base/read-file.hh"

// ----------------------------------------------------------------------

template <typename RW> class JsonWriterT : public RW
{
 public:
    inline JsonWriterT(std::string aKeyword) : RW(mBuffer), mKeyword(aKeyword) {}
    inline operator std::string() const { return mBuffer.GetString(); }
    inline std::string keyword() const { return mKeyword; }

 private:
    rapidjson::StringBuffer mBuffer;
    std::string mKeyword;
};

// template <> inline JsonWriterT<rapidjson::PrettyWriter<rapidjson::StringBuffer>>::JsonWriterT(std::string aKeyword)
//     : rapidjson::PrettyWriter<rapidjson::StringBuffer>(mBuffer), mKeyword(aKeyword)
// {
//     SetIndent(' ', 1);
// }

typedef JsonWriterT<rapidjson::Writer<rapidjson::StringBuffer>> JsonWriter;
typedef JsonWriterT<rapidjson::PrettyWriter<rapidjson::StringBuffer>> JsonPrettyWriter;

// ----------------------------------------------------------------------

enum _StartArray { StartArray };
enum _EndArray { EndArray };
enum _StartObject { StartObject };
enum _EndObject { EndObject };

template <typename RW> inline JsonWriterT<RW>& operator <<(JsonWriterT<RW>& writer, _StartArray) { writer.StartArray(); return writer; }
template <typename RW> inline JsonWriterT<RW>& operator <<(JsonWriterT<RW>& writer, _EndArray) { writer.EndArray(); return writer; }
template <typename RW> inline JsonWriterT<RW>& operator <<(JsonWriterT<RW>& writer, _StartObject) { writer.StartObject(); return writer; }
template <typename RW> inline JsonWriterT<RW>& operator <<(JsonWriterT<RW>& writer, _EndObject) { writer.EndObject(); return writer; }

template <typename RW> inline JsonWriterT<RW>& operator <<(JsonWriterT<RW>& writer, const char* s) { writer.String(s, static_cast<unsigned>(strlen(s))); return writer; }
template <typename RW> inline JsonWriterT<RW>& operator <<(JsonWriterT<RW>& writer, std::string s) { writer.String(s.c_str(), static_cast<unsigned>(s.size())); return writer; }
template <typename RW> inline JsonWriterT<RW>& operator <<(JsonWriterT<RW>& writer, int value) { writer.Int(value); return writer; }
template <typename RW> inline JsonWriterT<RW>& operator <<(JsonWriterT<RW>& writer, double value) { writer.Double(value); return writer; }

class JsonObjectKey
{
 public:
    inline JsonObjectKey(const char* v) : value(v) {}
    inline JsonObjectKey(std::string v) : value(v.c_str()) {}
    inline operator const char*() const { return value; }

 private:
    const char* value;
};

template <typename RW> inline JsonWriterT<RW>& operator <<(JsonWriterT<RW>& writer, JsonObjectKey value) { writer.Key(value); return writer; }

// ----------------------------------------------------------------------

template <typename RW, typename T> inline JsonWriterT<RW>& operator <<(JsonWriterT<RW>& writer, const std::vector<T>& list)
{
    writer << StartArray;
    for (const auto& e: list)
        writer << e;
    return writer << EndArray;
}

// ----------------------------------------------------------------------

template <typename RW> inline JsonWriterT<RW>& operator <<(JsonWriterT<RW>& writer, const std::vector<std::vector<std::string>>& list_list_strings)
{
    writer << StartArray;
    for (const auto& e: list_list_strings)
        writer << e;
    return writer << EndArray;
}

// ----------------------------------------------------------------------

template <typename RW> inline JsonWriterT<RW>& operator <<(JsonWriterT<RW>& writer, const std::map<std::string, std::vector<std::string>>& map_list_strings)
{
    writer << StartObject;
    for (const auto& e: map_list_strings)
        writer << JsonObjectKey(e.first) << e.second;
    return writer << EndObject;
}

// ----------------------------------------------------------------------

namespace json_writer { namespace _internal
{
      // void_t is a C++17 feature
    template<class ...> using void_t = void; // http://stackoverflow.com/questions/26513095/void-t-can-implement-concepts
    template <typename T, typename = void> struct castable_to_char : public std::false_type {};
    template <typename T> struct castable_to_char<T, void_t<decltype(static_cast<char>(std::declval<T>()))>> : public std::true_type {};
}}

template <typename RW, typename Key, typename std::enable_if<json_writer::_internal::castable_to_char<Key>{}>::type* = nullptr>
                     inline JsonWriterT<RW>& operator <<(JsonWriterT<RW>& writer, Key key)
{
    const char k = static_cast<char>(key);
    writer.Key(&k, 1, false);
    return writer;
}

// ----------------------------------------------------------------------
// ----------------------------------------------------------------------

template <typename Key, typename Value> class _if_not_empty
{
 public:
    inline _if_not_empty(Key&& key, Value&& value) : mKey(key), mValue(value) {}

    template <typename RW> friend inline JsonWriterT<RW>& operator <<(JsonWriterT<RW>& writer, const _if_not_empty<Key, Value>& data)
        {
            if (!data.mValue.empty())
                writer << data.mKey << data.mValue;
            return writer;
        }

 private:
    Key mKey;
    Value mValue;
};

template <typename Key, typename Value> inline auto if_not_empty(Key&& key, Value&& value) { return _if_not_empty<Key, Value>(std::forward<Key>(key), std::forward<Value>(value)); }

// ----------------------------------------------------------------------
// ----------------------------------------------------------------------

template <typename V> inline std::string pretty_json(const V& value, std::string keyword, size_t indent, bool insert_emacs_indent_hint)
{
    JsonPrettyWriter writer(keyword);
    writer.SetIndent(' ', static_cast<unsigned int>(indent));
    writer << value;
    std::string result = writer;
{'fraction_non_alphanumeric': 0.24254437336697, 'fraction_numerical': 0.0011712766915938373, 'mean_word_length': 2.8118131868131866, 'pattern_counts': {'":': 0, '<': 257, '<?xml version=': 0, '>': 149, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1255693', 'n_tokens_mistral': 3115, 'n_tokens_neox': 3083, 'n_words': 788}