catch_jsonwriter.cpp
1 2 // Copyright Catch2 Authors 3 // Distributed under the Boost Software License, Version 1.0. 4 // (See accompanying file LICENSE.txt or copy at 5 // https://www.boost.org/LICENSE_1_0.txt) 6 7 // SPDX-License-Identifier: BSL-1.0 8 #include <catch2/internal/catch_enforce.hpp> 9 #include <catch2/internal/catch_jsonwriter.hpp> 10 11 namespace Catch { 12 void JsonUtils::indent( std::ostream& os, std::uint64_t level ) { 13 for ( std::uint64_t i = 0; i < level; ++i ) { 14 os << " "; 15 } 16 } 17 void JsonUtils::appendCommaNewline( std::ostream& os, 18 bool& should_comma, 19 std::uint64_t level ) { 20 if ( should_comma ) { os << ','; } 21 should_comma = true; 22 os << '\n'; 23 indent( os, level ); 24 } 25 26 JsonObjectWriter::JsonObjectWriter( std::ostream& os ): 27 JsonObjectWriter{ os, 0 } {} 28 29 JsonObjectWriter::JsonObjectWriter( std::ostream& os, 30 std::uint64_t indent_level ): 31 m_os{ os }, m_indent_level{ indent_level } { 32 m_os << '{'; 33 } 34 JsonObjectWriter::JsonObjectWriter( JsonObjectWriter&& source ): 35 m_os{ source.m_os }, 36 m_indent_level{ source.m_indent_level }, 37 m_should_comma{ source.m_should_comma }, 38 m_active{ source.m_active } { 39 source.m_active = false; 40 } 41 42 JsonObjectWriter::~JsonObjectWriter() { 43 if ( !m_active ) { return; } 44 45 m_os << '\n'; 46 JsonUtils::indent( m_os, m_indent_level ); 47 m_os << '}'; 48 } 49 50 JsonValueWriter JsonObjectWriter::write( StringRef key ) { 51 JsonUtils::appendCommaNewline( 52 m_os, m_should_comma, m_indent_level + 1 ); 53 54 m_os << '"' << key << "\": "; 55 return JsonValueWriter{ m_os, m_indent_level + 1 }; 56 } 57 58 JsonArrayWriter::JsonArrayWriter( std::ostream& os ): 59 JsonArrayWriter{ os, 0 } {} 60 JsonArrayWriter::JsonArrayWriter( std::ostream& os, 61 std::uint64_t indent_level ): 62 m_os{ os }, m_indent_level{ indent_level } { 63 m_os << '['; 64 } 65 JsonArrayWriter::JsonArrayWriter( JsonArrayWriter&& source ): 66 m_os{ source.m_os }, 67 m_indent_level{ source.m_indent_level }, 68 m_should_comma{ source.m_should_comma }, 69 m_active{ source.m_active } { 70 source.m_active = false; 71 } 72 JsonArrayWriter::~JsonArrayWriter() { 73 if ( !m_active ) { return; } 74 75 m_os << '\n'; 76 JsonUtils::indent( m_os, m_indent_level ); 77 m_os << ']'; 78 } 79 80 JsonObjectWriter JsonArrayWriter::writeObject() { 81 JsonUtils::appendCommaNewline( 82 m_os, m_should_comma, m_indent_level + 1 ); 83 return JsonObjectWriter{ m_os, m_indent_level + 1 }; 84 } 85 86 JsonArrayWriter JsonArrayWriter::writeArray() { 87 JsonUtils::appendCommaNewline( 88 m_os, m_should_comma, m_indent_level + 1 ); 89 return JsonArrayWriter{ m_os, m_indent_level + 1 }; 90 } 91 92 JsonArrayWriter& JsonArrayWriter::write( bool value ) { 93 return writeImpl( value ); 94 } 95 96 JsonValueWriter::JsonValueWriter( std::ostream& os ): 97 JsonValueWriter{ os, 0 } {} 98 99 JsonValueWriter::JsonValueWriter( std::ostream& os, 100 std::uint64_t indent_level ): 101 m_os{ os }, m_indent_level{ indent_level } {} 102 103 JsonObjectWriter JsonValueWriter::writeObject() && { 104 return JsonObjectWriter{ m_os, m_indent_level }; 105 } 106 107 JsonArrayWriter JsonValueWriter::writeArray() && { 108 return JsonArrayWriter{ m_os, m_indent_level }; 109 } 110 111 void JsonValueWriter::write( Catch::StringRef value ) && { 112 writeImpl( value, true ); 113 } 114 115 void JsonValueWriter::write( bool value ) && { 116 writeImpl( value ? "true"_sr : "false"_sr, false ); 117 } 118 119 void JsonValueWriter::writeImpl( Catch::StringRef value, bool quote ) { 120 if ( quote ) { m_os << '"'; } 121 for (char c : value) { 122 // Escape list taken from https://www.json.org/json-en.html, 123 // string definition. 124 // Note that while forward slash _can_ be escaped, it does 125 // not have to be, if JSON is not further embedded somewhere 126 // where forward slash is meaningful. 127 if ( c == '"' ) { 128 m_os << "\\\""; 129 } else if ( c == '\\' ) { 130 m_os << "\\\\"; 131 } else if ( c == '\b' ) { 132 m_os << "\\b"; 133 } else if ( c == '\f' ) { 134 m_os << "\\f"; 135 } else if ( c == '\n' ) { 136 m_os << "\\n"; 137 } else if ( c == '\r' ) { 138 m_os << "\\r"; 139 } else if ( c == '\t' ) { 140 m_os << "\\t"; 141 } else { 142 m_os << c; 143 } 144 } 145 if ( quote ) { m_os << '"'; } 146 } 147 148 } // namespace Catch