1 //===--- StringSwitch.h - Switch-on-literal-string Construct --------------===/
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //===----------------------------------------------------------------------===/
7 //
8 //  This file implements the StringSwitch template, which mimics a switch()
9 //  statement whose cases are string literals.
10 //
11 //===----------------------------------------------------------------------===/
12 #ifndef LLVM_ADT_STRINGSWITCH_H
13 #define LLVM_ADT_STRINGSWITCH_H
14 
15 #include "llvm/ADT/StringRef.h"
16 #include "llvm/Support/Compiler.h"
17 #include <cassert>
18 #include <cstring>
19 
20 namespace llvm {
21 
22 /// A switch()-like statement whose cases are string literals.
23 ///
24 /// The StringSwitch class is a simple form of a switch() statement that
25 /// determines whether the given string matches one of the given string
26 /// literals. The template type parameter \p T is the type of the value that
27 /// will be returned from the string-switch expression. For example,
28 /// the following code switches on the name of a color in \c argv[i]:
29 ///
30 /// \code
31 /// Color color = StringSwitch<Color>(argv[i])
32 ///   .Case("red", Red)
33 ///   .Case("orange", Orange)
34 ///   .Case("yellow", Yellow)
35 ///   .Case("green", Green)
36 ///   .Case("blue", Blue)
37 ///   .Case("indigo", Indigo)
38 ///   .Cases("violet", "purple", Violet)
39 ///   .Default(UnknownColor);
40 /// \endcode
41 template<typename T, typename R = T>
42 class StringSwitch {
43   /// The string we are matching.
44   const StringRef Str;
45 
46   /// The pointer to the result of this switch statement, once known,
47   /// null before that.
48   Optional<T> Result;
49 
50 public:
51   explicit StringSwitch(StringRef S)
52   : Str(S), Result() { }
53 
54   // StringSwitch is not copyable.
55   StringSwitch(const StringSwitch &) = delete;
56 
57   // StringSwitch is not assignable due to 'Str' being 'const'.
58   void operator=(const StringSwitch &) = delete;
59   void operator=(StringSwitch &&other) = delete;
60 
61   StringSwitch(StringSwitch &&other)
62     : Str(other.Str), Result(std::move(other.Result)) { }
63 
64   ~StringSwitch() = default;
65 
66   // Case-sensitive case matchers
67   StringSwitch &Case(StringLiteral S, T Value) {
68     if (!Result && Str == S) {
69       Result = std::move(Value);
70     }
71     return *this;
72   }
73 
74   StringSwitch& EndsWith(StringLiteral S, T Value) {
75     if (!Result && Str.endswith(S)) {
76       Result = std::move(Value);
77     }
78     return *this;
79   }
80 
81   StringSwitch& StartsWith(StringLiteral S, T Value) {
82     if (!Result && Str.startswith(S)) {
83       Result = std::move(Value);
84     }
85     return *this;
86   }
87 
88   StringSwitch &Cases(StringLiteral S0, StringLiteral S1, T Value) {
89     return Case(S0, Value).Case(S1, Value);
90   }
91 
92   StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2,
93                       T Value) {
94     return Case(S0, Value).Cases(S1, S2, Value);
95   }
96 
97   StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2,
98                       StringLiteral S3, T Value) {
99     return Case(S0, Value).Cases(S1, S2, S3, Value);
100   }
101 
102   StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2,
103                       StringLiteral S3, StringLiteral S4, T Value) {
104     return Case(S0, Value).Cases(S1, S2, S3, S4, Value);
105   }
106 
107   StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2,
108                       StringLiteral S3, StringLiteral S4, StringLiteral S5,
109                       T Value) {
110     return Case(S0, Value).Cases(S1, S2, S3, S4, S5, Value);
111   }
112 
113   StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2,
114                       StringLiteral S3, StringLiteral S4, StringLiteral S5,
115                       StringLiteral S6, T Value) {
116     return Case(S0, Value).Cases(S1, S2, S3, S4, S5, S6, Value);
117   }
118 
119   StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2,
120                       StringLiteral S3, StringLiteral S4, StringLiteral S5,
121                       StringLiteral S6, StringLiteral S7, T Value) {
122     return Case(S0, Value).Cases(S1, S2, S3, S4, S5, S6, S7, Value);
123   }
124 
125   StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2,
126                       StringLiteral S3, StringLiteral S4, StringLiteral S5,
127                       StringLiteral S6, StringLiteral S7, StringLiteral S8,
128                       T Value) {
129     return Case(S0, Value).Cases(S1, S2, S3, S4, S5, S6, S7, S8, Value);
130   }
131 
132   StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2,
133                       StringLiteral S3, StringLiteral S4, StringLiteral S5,
134                       StringLiteral S6, StringLiteral S7, StringLiteral S8,
135                       StringLiteral S9, T Value) {
136     return Case(S0, Value).Cases(S1, S2, S3, S4, S5, S6, S7, S8, S9, Value);
137   }
138 
139   // Case-insensitive case matchers.
140   StringSwitch &CaseLower(StringLiteral S, T Value) {
141     if (!Result && Str.equals_lower(S))
142       Result = std::move(Value);
143 
144     return *this;
145   }
146 
147   StringSwitch &EndsWithLower(StringLiteral S, T Value) {
148     if (!Result && Str.endswith_lower(S))
149       Result = Value;
150 
151     return *this;
152   }
153 
154   StringSwitch &StartsWithLower(StringLiteral S, T Value) {
155     if (!Result && Str.startswith_lower(S))
156       Result = std::move(Value);
157 
158     return *this;
159   }
160 
161   StringSwitch &CasesLower(StringLiteral S0, StringLiteral S1, T Value) {
162     return CaseLower(S0, Value).CaseLower(S1, Value);
163   }
164 
165   StringSwitch &CasesLower(StringLiteral S0, StringLiteral S1, StringLiteral S2,
166                            T Value) {
167     return CaseLower(S0, Value).CasesLower(S1, S2, Value);
168   }
169 
170   StringSwitch &CasesLower(StringLiteral S0, StringLiteral S1, StringLiteral S2,
171                            StringLiteral S3, T Value) {
172     return CaseLower(S0, Value).CasesLower(S1, S2, S3, Value);
173   }
174 
175   StringSwitch &CasesLower(StringLiteral S0, StringLiteral S1, StringLiteral S2,
176                            StringLiteral S3, StringLiteral S4, T Value) {
177     return CaseLower(S0, Value).CasesLower(S1, S2, S3, S4, Value);
178   }
179 
180   LLVM_NODISCARD
181   R Default(T Value) {
182     if (Result)
183       return std::move(*Result);
184     return Value;
185   }
186 
187   LLVM_NODISCARD
188   operator R() {
189     assert(Result && "Fell off the end of a string-switch");
190     return std::move(*Result);
191   }
192 };
193 
194 } // end namespace llvm
195 
196 #endif // LLVM_ADT_STRINGSWITCH_H
197