about summary refs log tree commit diff
path: root/third_party/nix/src/libutil/json.hh
#pragma once

#include <cassert>
#include <iostream>
#include <vector>

namespace nix {

void toJSON(std::ostream& str, const char* start, const char* end);
void toJSON(std::ostream& str, const char* s);

template <typename T>
void toJSON(std::ostream& str, const T& n);

class JSONWriter {
 protected:
  struct JSONState {
    std::ostream& str;
    bool indent;
    size_t depth = 0;
    size_t stack = 0;
    JSONState(std::ostream& str, bool indent) : str(str), indent(indent) {}
    ~JSONState() { assert(stack == 0); }
  };

  JSONState* state;

  bool first = true;

  JSONWriter(std::ostream& str, bool indent);

  explicit JSONWriter(JSONState* state);

  ~JSONWriter();

  void assertActive() { assert(state->stack != 0); }

  void comma();

  void indent();
};

class JSONObject;
class JSONPlaceholder;

class JSONList : JSONWriter {
 private:
  friend class JSONObject;
  friend class JSONPlaceholder;

  void open();

  explicit JSONList(JSONState* state) : JSONWriter(state) { open(); }

 public:
  explicit JSONList(std::ostream& str, bool indent = false)
      : JSONWriter(str, indent) {
    open();
  }

  ~JSONList();

  template <typename T>
  JSONList& elem(const T& v) {
    comma();
    toJSON(state->str, v);
    return *this;
  }

  JSONList list();

  JSONObject object();

  JSONPlaceholder placeholder();
};

class JSONObject : JSONWriter {
 private:
  friend class JSONList;
  friend class JSONPlaceholder;

  void open();

  explicit JSONObject(JSONState* state) : JSONWriter(state) { open(); }

  void attr(const std::string& s);

 public:
  explicit JSONObject(std::ostream& str, bool indent = false)
      : JSONWriter(str, indent) {
    open();
  }

  JSONObject(const JSONObject& obj) = delete;

  JSONObject(JSONObject&& obj) : JSONWriter(obj.state) { obj.state = 0; }

  ~JSONObject();

  template <typename T>
  JSONObject& attr(const std::string& name, const T& v) {
    attr(name);
    toJSON(state->str, v);
    return *this;
  }

  JSONList list(const std::string& name);

  JSONObject object(const std::string& name);

  JSONPlaceholder placeholder(const std::string& name);
};

class JSONPlaceholder : JSONWriter {
 private:
  friend class JSONList;
  friend class JSONObject;

  explicit JSONPlaceholder(JSONState* state) : JSONWriter(state) {}

  void assertValid() {
    assertActive();
    assert(first);
  }

 public:
  explicit JSONPlaceholder(std::ostream& str, bool indent = false)
      : JSONWriter(str, indent) {}

  ~JSONPlaceholder() { assert(!first || std::uncaught_exception()); }

  template <typename T>
  void write(const T& v) {
    assertValid();
    first = false;
    toJSON(state->str, v);
  }

  JSONList list();

  JSONObject object();
};

}  // namespace nix