| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef TOOLS_FIDL_FIDLC_INCLUDE_FIDL_JSON_WRITER_H_ |
| #define TOOLS_FIDL_FIDLC_INCLUDE_FIDL_JSON_WRITER_H_ |
| |
| #include <lib/fit/function.h> |
| |
| #include <ostream> |
| #include <string_view> |
| #include <vector> |
| |
| namespace fidl::utils { |
| |
| // Methods or functions named "Emit..." are the actual protocol to |
| // the JSON output. |
| |
| // Other public methods take various value types and generate JSON |
| // output via the "Emit" routines. |
| |
| // |JsonWriter| requires the derived type as a template parameter so it can |
| // match methods declared with parameter overrides in the derived class. |
| template <typename DerivedT> |
| class JsonWriter { |
| public: |
| JsonWriter(std::ostream& os, int indent_level = 0) : os_(os), indent_level_(indent_level) {} |
| |
| ~JsonWriter() = default; |
| |
| template <typename Iterator> |
| void GenerateArray(Iterator begin, Iterator end) { |
| EmitArrayBegin(); |
| |
| if (begin != end) { |
| Indent(); |
| EmitNewlineWithIndent(); |
| } |
| |
| for (Iterator it = begin; it != end; ++it) { |
| if (it != begin) |
| EmitArraySeparator(); |
| self.Generate(*it); |
| } |
| |
| if (begin != end) { |
| Outdent(); |
| EmitNewlineWithIndent(); |
| } |
| |
| EmitArrayEnd(); |
| } |
| |
| template <typename Collection> |
| void GenerateArray(const Collection& collection) { |
| self.GenerateArray(collection.begin(), collection.end()); |
| } |
| |
| // Note that this overload will take precedence over Generate(const Base*) |
| // when given a Derived* argument. To avoid that, you must either static_cast |
| // to Base* or implement Generate(const Base&). |
| template <typename T> |
| void Generate(const T* value) { |
| self.Generate(*value); |
| } |
| |
| template <typename T> |
| void Generate(const std::unique_ptr<T>& value) { |
| self.Generate(*value); |
| } |
| |
| template <typename T> |
| void Generate(const std::shared_ptr<T>& value) { |
| self.Generate(*value); |
| } |
| |
| template <typename T> |
| void Generate(const std::vector<T>& value) { |
| self.GenerateArray(value); |
| } |
| |
| void Generate(bool value) { EmitBoolean(value); } |
| |
| void Generate(std::string_view value) { EmitString(value); } |
| |
| void Generate(std::string value) { EmitString(value); } |
| |
| void Generate(uint32_t value) { EmitNumeric(value); } |
| void Generate(int64_t value) { EmitNumeric(value); } |
| void Generate(uint64_t value) { EmitNumeric(value); } |
| |
| void ResetIndentLevel() { indent_level_ = 0; } |
| |
| void Indent() { indent_level_++; } |
| |
| void Outdent() { indent_level_--; } |
| |
| // Similar to |this| pointer, the |self| reference is simply pre-cast to the |
| // derived type. Methods called with template-typed parameters (such as |
| // this->Generate(obj)) would only see the Generate() methods and acceptable |
| // parameter types defined within this base class, but when called using |
| // self.Generate(obj), the type of obj can be matched to all Generate() |
| // methods declared by both the derived type and exposed from its base |
| // class (this class). |
| DerivedT& self = *static_cast<DerivedT*>(this); |
| |
| protected: |
| enum class Position { |
| kFirst, |
| kSubsequent, |
| }; |
| |
| // ConstantStyle indicates whether the constant value to be emitted should be |
| // directly placed in the JSON output, or whether is must be wrapped in a |
| // string. |
| enum ConstantStyle { |
| kAsConstant, |
| kAsString, |
| }; |
| |
| void GenerateEOF() { EmitNewline(); } |
| |
| void GenerateObjectPunctuation(Position position) { |
| switch (position) { |
| case Position::kFirst: |
| Indent(); |
| EmitNewlineWithIndent(); |
| break; |
| case Position::kSubsequent: |
| EmitObjectSeparator(); |
| break; |
| } |
| } |
| |
| void GenerateObject(fit::closure callback) { |
| int original_indent_level = indent_level_; |
| |
| EmitObjectBegin(); |
| |
| callback(); |
| |
| if (indent_level_ > original_indent_level) { |
| Outdent(); |
| EmitNewlineWithIndent(); |
| } |
| |
| EmitObjectEnd(); |
| } |
| |
| template <typename Type> |
| void GenerateObjectMember(std::string_view key, const Type& value, |
| Position position = Position::kSubsequent) { |
| GenerateObjectPunctuation(position); |
| EmitObjectKey(key); |
| self.Generate(value); |
| } |
| |
| void EmitBoolean(bool value, ConstantStyle style = kAsConstant) { |
| if (style == kAsString) |
| os_ << "\""; |
| if (value) |
| os_ << "true"; |
| else |
| os_ << "false"; |
| if (style == kAsString) |
| os_ << "\""; |
| } |
| |
| void EmitString(std::string_view value) { |
| os_ << "\""; |
| |
| for (size_t i = 0; i < value.size(); ++i) { |
| const char c = value[i]; |
| switch (c) { |
| case '"': |
| os_ << "\\\""; |
| break; |
| case '\\': |
| os_ << "\\\\"; |
| break; |
| case '\n': |
| os_ << "\\n"; |
| break; |
| // TODO(fxbug.dev/7365): Escape more characters. |
| default: |
| os_ << c; |
| break; |
| } |
| } |
| os_ << "\""; |
| } |
| |
| void EmitLiteral(std::string_view value) { os_.rdbuf()->sputn(value.data(), value.size()); } |
| |
| template <typename ValueType> |
| void EmitNumeric(ValueType value, ConstantStyle style = kAsConstant) { |
| static_assert(std::is_arithmetic<ValueType>::value && !std::is_same<ValueType, bool>::value, |
| "EmitNumeric can only be used with a numeric ValueType"); |
| static_assert(std::is_arithmetic<ValueType>::value && !std::is_same<ValueType, uint8_t>::value, |
| "EmitNumeric does not work for uint8_t, upcast to uint64_t"); |
| static_assert(std::is_arithmetic<ValueType>::value && !std::is_same<ValueType, int8_t>::value, |
| "EmitNumeric does not work for int8_t, upcast to int64_t"); |
| |
| switch (style) { |
| case ConstantStyle::kAsConstant: |
| os_ << value; |
| break; |
| case ConstantStyle::kAsString: |
| os_ << "\"" << value << "\""; |
| break; |
| } |
| } |
| |
| void EmitNewline() { os_ << "\n"; } |
| |
| void EmitNewlineWithIndent() { |
| os_ << "\n"; |
| int indent_level = indent_level_; |
| while (indent_level--) |
| os_ << kIndent; |
| } |
| |
| void EmitObjectBegin() { os_ << "{"; } |
| |
| void EmitObjectSeparator() { |
| os_ << ","; |
| EmitNewlineWithIndent(); |
| } |
| |
| void EmitObjectEnd() { os_ << "}"; } |
| |
| void EmitObjectKey(std::string_view key) { |
| EmitString(key); |
| os_ << ": "; |
| } |
| |
| void EmitArrayBegin() { os_ << "["; } |
| |
| void EmitArraySeparator() { |
| os_ << ","; |
| EmitNewlineWithIndent(); |
| } |
| |
| void EmitArrayEnd() { os_ << "]"; } |
| |
| private: |
| static constexpr const char* kIndent = " "; |
| std::ostream& os_; |
| int indent_level_; |
| }; |
| |
| } // namespace fidl::utils |
| |
| #endif // TOOLS_FIDL_FIDLC_INCLUDE_FIDL_JSON_WRITER_H_ |