1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
|
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
#ifndef CATCH_XMLWRITER_HPP_INCLUDED
#define CATCH_XMLWRITER_HPP_INCLUDED
#include <catch2/internal/catch_reusable_string_stream.hpp>
#include <catch2/internal/catch_stringref.hpp>
#include <iosfwd>
#include <vector>
#include <cstdint>
namespace Catch {
enum class XmlFormatting : std::uint8_t {
None = 0x00,
Indent = 0x01,
Newline = 0x02,
};
constexpr XmlFormatting operator|( XmlFormatting lhs, XmlFormatting rhs ) {
return static_cast<XmlFormatting>( static_cast<std::uint8_t>( lhs ) |
static_cast<std::uint8_t>( rhs ) );
}
constexpr XmlFormatting operator&( XmlFormatting lhs, XmlFormatting rhs ) {
return static_cast<XmlFormatting>( static_cast<std::uint8_t>( lhs ) &
static_cast<std::uint8_t>( rhs ) );
}
/**
* Helper for XML-encoding text (escaping angle brackets, quotes, etc)
*
* Note: doesn't take ownership of passed strings, and thus the
* encoded string must outlive the encoding instance.
*/
class XmlEncode {
public:
enum ForWhat { ForTextNodes, ForAttributes };
constexpr XmlEncode( StringRef str, ForWhat forWhat = ForTextNodes ):
m_str( str ), m_forWhat( forWhat ) {}
void encodeTo( std::ostream& os ) const;
friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode );
private:
StringRef m_str;
ForWhat m_forWhat;
};
class XmlWriter {
public:
class ScopedElement {
public:
ScopedElement( XmlWriter* writer, XmlFormatting fmt );
ScopedElement( ScopedElement&& other ) noexcept;
ScopedElement& operator=( ScopedElement&& other ) noexcept;
~ScopedElement();
ScopedElement&
writeText( StringRef text,
XmlFormatting fmt = XmlFormatting::Newline |
XmlFormatting::Indent );
ScopedElement& writeAttribute( StringRef name,
StringRef attribute );
template <typename T,
// Without this SFINAE, this overload is a better match
// for `std::string`, `char const*`, `char const[N]` args.
// While it would still work, it would cause code bloat
// and multiple iteration over the strings
typename = typename std::enable_if_t<
!std::is_convertible<T, StringRef>::value>>
ScopedElement& writeAttribute( StringRef name,
T const& attribute ) {
m_writer->writeAttribute( name, attribute );
return *this;
}
private:
XmlWriter* m_writer = nullptr;
XmlFormatting m_fmt;
};
XmlWriter( std::ostream& os );
~XmlWriter();
XmlWriter( XmlWriter const& ) = delete;
XmlWriter& operator=( XmlWriter const& ) = delete;
XmlWriter& startElement( std::string const& name, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
ScopedElement scopedElement( std::string const& name, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
XmlWriter& endElement(XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
//! The attribute content is XML-encoded
XmlWriter& writeAttribute( StringRef name, StringRef attribute );
//! Writes the attribute as "true/false"
XmlWriter& writeAttribute( StringRef name, bool attribute );
//! The attribute content is XML-encoded
XmlWriter& writeAttribute( StringRef name, char const* attribute );
//! The attribute value must provide op<<(ostream&, T). The resulting
//! serialization is XML-encoded
template <typename T,
// Without this SFINAE, this overload is a better match
// for `std::string`, `char const*`, `char const[N]` args.
// While it would still work, it would cause code bloat
// and multiple iteration over the strings
typename = typename std::enable_if_t<
!std::is_convertible<T, StringRef>::value>>
XmlWriter& writeAttribute( StringRef name, T const& attribute ) {
ReusableStringStream rss;
rss << attribute;
return writeAttribute( name, rss.str() );
}
//! Writes escaped `text` in a element
XmlWriter& writeText( StringRef text,
XmlFormatting fmt = XmlFormatting::Newline |
XmlFormatting::Indent );
//! Writes XML comment as "<!-- text -->"
XmlWriter& writeComment( StringRef text,
XmlFormatting fmt = XmlFormatting::Newline |
XmlFormatting::Indent );
void writeStylesheetRef( StringRef url );
void ensureTagClosed();
private:
void applyFormatting(XmlFormatting fmt);
void writeDeclaration();
void newlineIfNecessary();
bool m_tagIsOpen = false;
bool m_needsNewline = false;
std::vector<std::string> m_tags;
std::string m_indent;
std::ostream& m_os;
};
}
#endif // CATCH_XMLWRITER_HPP_INCLUDED
|