1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qqmldomstringdumper_p.h"
5#include <QtCore/QDebug>
6
7QT_BEGIN_NAMESPACE
8
9namespace QQmlJS {
10namespace Dom {
11
12/*!
13 * \internal
14 * \tn QQmlJS::Dom::Sink
15 * \brief A Sink is a function that accepts a QStringView as input
16 *
17 * A Sink it the core element of a text based stream oriented output.
18 * It is simply a function accepting a QStringView as input.
19 */
20
21/*!
22 * \internal
23 * \tn QQmlJS::Dom::sinkInt
24 * \brief writes an integer to a sink without any axtra heap allocation
25 * \param sink where to write
26 * \param i the integer to write out
27 *
28 * mainly for debugging/fatal errors
29 */
30
31/*!
32 * \internal
33 * \class QQmlJS::Dom::Dumper
34 * \brief Helper class to accept eithe a string or a dumper (a function that writes to a sink)
35 *
36 * Using a Dumper as input parameter one always obtains a dumper (i.e. a
37 * function_ref<void(function_ref<void(QStringView)>)> , but can pass in any
38 * object accepted by QStringView, and it is automatically converted to a dumper.
39 */
40
41/*!
42 * \internal
43 * \brief Converts a dumper to a string
44 * \param writer The dumper convert to a string
45 */
46QString dumperToString(Dumper writer)
47{
48 QString s;
49 QTextStream d(&s);
50 writer([&d](QStringView s){ d << s; });
51 d.flush();
52 return s;
53}
54
55/*!
56 * \internal
57 * \brief dumps a string as quoted string (escaping things like quotes or newlines)
58 * \param sink The sink to write the quoted string to
59 * \param s The string to sink
60 * \param options If quotes should be outputted around the string (defaults to yes)
61 */
62void sinkEscaped(Sink sink, QStringView s, EscapeOptions options) {
63 if (options == EscapeOptions::OuterQuotes)
64 sink(u"\"");
65 int it0=0;
66 for (int it = 0; it < s.size();++it) {
67 QChar c=s[it];
68 bool noslash = c != QLatin1Char('\\');
69 bool noquote = c != QLatin1Char('"');
70 bool nonewline = c != QLatin1Char('\n');
71 bool noreturn = c != QLatin1Char('\r');
72 if (noslash && noquote && nonewline && noreturn)
73 continue;
74 sink(s.mid(pos: it0, n: it - it0));
75 it0 = it + 1;
76 if (!noslash)
77 sink(u"\\\\");
78 else if (!noquote)
79 sink(u"\\\"");
80 else if (!nonewline)
81 sink(u"\\n");
82 else if (!noreturn)
83 sink(u"\\r");
84 else
85 Q_ASSERT(0);
86 }
87 sink(s.mid(pos: it0, n: s.size() - it0));
88 if (options == EscapeOptions::OuterQuotes)
89 sink(u"\"");
90}
91
92/*!
93 * \internal
94 * \brief Dumps a string describing the given error level (ErrorLevel::Error -> Error,...)
95 * \param s the sink to write to
96 * \param level the level to describe
97 */
98void dumpErrorLevel(Sink s, ErrorLevel level)
99{
100 switch (level) {
101 case ErrorLevel::Debug:
102 s(u"Debug");
103 break;
104 case ErrorLevel::Info:
105 s(u"Info");
106 break;
107 case ErrorLevel::Warning:
108 s(u"Warning");
109 break;
110 case ErrorLevel::Error:
111 s(u"Error");
112 break;
113 case ErrorLevel::Fatal:
114 s(u"Fatal");
115 break;
116 }
117
118}
119
120void dumperToQDebug(Dumper dumper, QDebug debug)
121{
122 QDebug & d = debug.noquote().nospace();
123 dumper([&d](QStringView s){
124 d << s;
125 });
126}
127
128/*!
129 * \internal
130 * \brief writes the dumper to the QDebug object corrsponding to the given error level
131 * \param level the error level of the message
132 * \param dumper the dumper that writes a message
133 */
134void dumperToQDebug(Dumper dumper, ErrorLevel level)
135{
136 QDebug d = qDebug().noquote().nospace();
137 switch (level) {
138 case ErrorLevel::Debug:
139 break;
140 case ErrorLevel::Info:
141 d = qInfo().noquote().nospace();
142 break;
143 case ErrorLevel::Warning:
144 d = qWarning().noquote().nospace();
145 break;
146 case ErrorLevel::Error:
147 case ErrorLevel::Fatal: // should be handled differently (avoid allocations...), we try to catch them before ending up here
148 d = qCritical().noquote().nospace();
149 break;
150 }
151 dumper([&d](QStringView s){
152 d << s;
153 });
154}
155
156/*!
157 * \internal
158 * \brief sinks the requested amount of spaces
159 */
160void sinkIndent(Sink s, int indent)
161{
162 if (indent > 0) {
163 QStringView spaces = u" ";
164 while (indent > spaces.size()) {
165 s(spaces);
166 indent -= spaces.size();
167 }
168 s(spaces.left(n: indent));
169 }
170}
171
172/*!
173 * \internal
174 * \brief sinks a neline and indents by the given amount
175 */
176void sinkNewline(Sink s, int indent)
177{
178 s(u"\n");
179 if (indent > 0)
180 sinkIndent(s, indent);
181}
182
183/*!
184 * \internal
185 * \fn QQmlJS::Dom::devNull
186 * \brief A sink that ignores whatever it receives
187 */
188
189QDebug operator<<(QDebug d, Dumper dumper)
190{
191 QDebug dd = d.noquote().nospace();
192 dumper([&dd](QStringView s) { dd << s; });
193 return d;
194}
195
196} // end namespace Dom
197} // end namespace QQmlJS
198
199QT_END_NAMESPACE
200

source code of qtdeclarative/src/qmldom/qqmldomstringdumper.cpp