| 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(const 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(const 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(const 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(const 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(const 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(const 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(const 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, const 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 | |