1 /*
2     This file is part of Konsole, a terminal emulator for KDE.
3 
4     SPDX-FileCopyrightText: 2018 Mariusz Glebocki <mglb@arccos-1.net>
5 
6     SPDX-License-Identifier: GPL-2.0-or-later
7 */
8 
9 #ifndef TEMPLATE_H
10 #define TEMPLATE_H
11 
12 #include <QMap>
13 #include <QString>
14 #include <QVector>
15 
16 // QVariant doesn't offer modification in place. Var does.
17 class Var
18 {
19 public:
20     using Number = qint64;
21     using String = QString;
22     using Map = QMap<String, Var>;
23     using Vector = QVector<Var>;
24 
25     enum class DataType {
26         Invalid,
27         Number,
28         String,
29         Vector,
30         Map,
31     };
32 
dataTypeAsString()33     const QString dataTypeAsString() const
34     {
35         switch (dataType()) {
36         case DataType::Invalid:
37             return QStringLiteral("Invalid");
38         case DataType::Number:
39             return QStringLiteral("Number");
40         case DataType::String:
41             return QStringLiteral("String");
42         case DataType::Vector:
43             return QStringLiteral("Vector");
44         case DataType::Map:
45             return QStringLiteral("Map");
46         default:
47             return QStringLiteral("Unknown?");
48         }
49     }
50 
Var()51     Var()
52         : num(0)
53         , _dataType(DataType::Invalid)
54     {
55     }
Var(const Var & other)56     Var(const Var &other)
57     {
58         *this = other;
59     }
60 
Var(const Number & newNum)61     Var(const Number &newNum)
62         : _dataType(DataType::Number)
63     {
64         new (&num) auto(newNum);
65     }
Var(const String & newStr)66     Var(const String &newStr)
67         : _dataType(DataType::String)
68     {
69         new (&str) auto(newStr);
70     }
Var(const Vector & newVec)71     Var(const Vector &newVec)
72         : _dataType(DataType::Vector)
73     {
74         new (&vec) auto(newVec);
75     }
Var(const Map & newMap)76     Var(const Map &newMap)
77         : _dataType(DataType::Map)
78     {
79         new (&map) auto(newMap);
80     }
81 
82     // Allow initialization without type name
Var(const char * newStr)83     Var(const char *newStr)
84         : _dataType(DataType::String)
85     {
86         new (&str) String(QString::fromUtf8(newStr));
87     }
Var(std::initializer_list<Var> newVec)88     Var(std::initializer_list<Var> newVec)
89         : _dataType(DataType::Vector)
90     {
91         new (&vec) Vector(newVec);
92     }
93 
~Var()94     ~Var()
95     {
96         switch (dataType()) {
97         case DataType::String:
98             str.~String();
99             break;
100         case DataType::Vector:
101             vec.~Vector();
102             break;
103         case DataType::Map:
104             map.~Map();
105             break;
106         default:
107             break;
108         }
109     }
110 
111     Var &operator=(const Var &other)
112     {
113         _dataType = other.dataType();
114         switch (other.dataType()) {
115         case DataType::Number:
116             new (&num) auto(other.num);
117             break;
118         case DataType::String:
119             new (&str) auto(other.str);
120             break;
121         case DataType::Vector:
122             new (&vec) auto(other.vec);
123             break;
124         case DataType::Map:
125             new (&map) auto(other.map);
126             break;
127         default:
128             break;
129         }
130         return *this;
131     }
132 
133     Var &operator[](unsigned index)
134     {
135         Q_ASSERT(_dataType == DataType::Vector);
136         return vec.data()[index];
137     }
138     const Var &operator[](unsigned index) const
139     {
140         Q_ASSERT(_dataType == DataType::Vector);
141         return vec.constData()[index];
142     }
143     Var &operator[](const String &key)
144     {
145         Q_ASSERT(_dataType == DataType::Map);
146         return map[key];
147     }
148     const Var &operator[](const String &key) const
149     {
150         Q_ASSERT(_dataType == DataType::Map);
151         return *map.find(key);
152     }
153 
dataType()154     DataType dataType() const
155     {
156         return _dataType;
157     }
158 
159     union {
160         Number num;
161         String str;
162         Vector vec;
163         Map map;
164     };
165 
166 private:
167     DataType _dataType;
168 };
169 
170 class Template
171 {
172 public:
173     Template(const QString &text);
174     void parse();
175     QString generate(const Var &data);
176 
177     struct Element {
178         Element(const Element *parent = nullptr, const QString &name = QString())
outerElement179             : outer()
180             , inner()
181             , name(name)
182             , fmt()
183             , line(0)
184             , column(0)
185             , isComment(false)
186             , children()
187             , parent(parent)
188         {
189         }
190 
ElementElement191         Element(const Element &other)
192             : outer(other.outer)
193             , inner(other.inner)
194             , name(other.name)
195             , fmt(other.fmt)
196             , line(other.line)
197             , column(other.column)
198             , isComment(other.isComment)
199             , parent(other.parent)
200         {
201             for (const auto &child : other.children) {
202                 children.append(child);
203             }
204         }
205 
206         const QString findFmt(Var::DataType type) const;
207         QString path() const;
isCommandElement208         bool isCommand() const
209         {
210             return name.startsWith(QLatin1Char('!'));
211         }
hasNameElement212         bool hasName() const
213         {
214             return !isCommand() && !name.isEmpty();
215         }
216 
217         static const QString defaultFmt(Var::DataType type);
218         static bool isValidFmt(const QString &fmt, Var::DataType type);
219 
220         QStringRef outer;
221         QStringRef inner;
222         QString name;
223         QString fmt;
224         uint line;
225         uint column;
226         bool isComment;
227         QList<Element> children;
228         const Element *parent;
229     };
230 
231 private:
232     void executeCommand(Element &element, const Element &childStub, const QStringList &argv);
233     void parseRecursively(Element &element);
234     int generateRecursively(QString &result, const Element &element, const Var &data, int consumed = 0);
235 
236     QString _text; // FIXME: make it pointer (?)
237     Element _root; // FIXME: make it pointer
238 };
239 
240 #endif
241