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 | |
7 | QT_BEGIN_NAMESPACE |
8 | |
9 | namespace QQmlJS { |
10 | namespace 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 | */ |
46 | QString 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 | */ |
62 | void 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 | */ |
98 | void 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 | |
120 | void 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 | */ |
134 | void 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 | */ |
160 | void 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 | */ |
176 | void 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 | |
189 | QDebug 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 | |
199 | QT_END_NAMESPACE |
200 | |