xref: /freebsd/contrib/llvm-project/llvm/include/llvm/ADT/StringSwitch.h (revision 700637cbb5e582861067a11aaca4d053546871d2)
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 /// \file
9 ///  This file implements the StringSwitch template, which mimics a switch()
10 ///  statement whose cases are string literals.
11 ///
12 //===----------------------------------------------------------------------===/
13 #ifndef LLVM_ADT_STRINGSWITCH_H
14 #define LLVM_ADT_STRINGSWITCH_H
15 
16 #include "llvm/ADT/StringRef.h"
17 #include <cassert>
18 #include <cstring>
19 #include <optional>
20 
21 namespace llvm {
22 
23 /// A switch()-like statement whose cases are string literals.
24 ///
25 /// The StringSwitch class is a simple form of a switch() statement that
26 /// determines whether the given string matches one of the given string
27 /// literals. The template type parameter \p T is the type of the value that
28 /// will be returned from the string-switch expression. For example,
29 /// the following code switches on the name of a color in \c argv[i]:
30 ///
31 /// \code
32 /// Color color = StringSwitch<Color>(argv[i])
33 ///   .Case("red", Red)
34 ///   .Case("orange", Orange)
35 ///   .Case("yellow", Yellow)
36 ///   .Case("green", Green)
37 ///   .Case("blue", Blue)
38 ///   .Case("indigo", Indigo)
39 ///   .Cases("violet", "purple", Violet)
40 ///   .Default(UnknownColor);
41 /// \endcode
42 template<typename T, typename R = T>
43 class StringSwitch {
44   /// The string we are matching.
45   const StringRef Str;
46 
47   /// The pointer to the result of this switch statement, once known,
48   /// null before that.
49   std::optional<T> Result;
50 
51 public:
StringSwitch(StringRef S)52   explicit StringSwitch(StringRef S)
53   : Str(S), Result() { }
54 
55   // StringSwitch is not copyable.
56   StringSwitch(const StringSwitch &) = delete;
57 
58   // StringSwitch is not assignable due to 'Str' being 'const'.
59   void operator=(const StringSwitch &) = delete;
60   void operator=(StringSwitch &&other) = delete;
61 
StringSwitch(StringSwitch && other)62   StringSwitch(StringSwitch &&other)
63     : Str(other.Str), Result(std::move(other.Result)) { }
64 
65   ~StringSwitch() = default;
66 
67   // Case-sensitive case matchers
Case(StringLiteral S,T Value)68   StringSwitch &Case(StringLiteral S, T Value) {
69     CaseImpl(Value, S);
70     return *this;
71   }
72 
EndsWith(StringLiteral S,T Value)73   StringSwitch& EndsWith(StringLiteral S, T Value) {
74     if (!Result && Str.ends_with(S)) {
75       Result = std::move(Value);
76     }
77     return *this;
78   }
79 
StartsWith(StringLiteral S,T Value)80   StringSwitch& StartsWith(StringLiteral S, T Value) {
81     if (!Result && Str.starts_with(S)) {
82       Result = std::move(Value);
83     }
84     return *this;
85   }
86 
Cases(StringLiteral S0,StringLiteral S1,T Value)87   StringSwitch &Cases(StringLiteral S0, StringLiteral S1, T Value) {
88     return CasesImpl(Value, S0, S1);
89   }
90 
Cases(StringLiteral S0,StringLiteral S1,StringLiteral S2,T Value)91   StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2,
92                       T Value) {
93     return CasesImpl(Value, S0, S1, S2);
94   }
95 
Cases(StringLiteral S0,StringLiteral S1,StringLiteral S2,StringLiteral S3,T Value)96   StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2,
97                       StringLiteral S3, T Value) {
98     return CasesImpl(Value, S0, S1, S2, S3);
99   }
100 
Cases(StringLiteral S0,StringLiteral S1,StringLiteral S2,StringLiteral S3,StringLiteral S4,T Value)101   StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2,
102                       StringLiteral S3, StringLiteral S4, T Value) {
103     return CasesImpl(Value, S0, S1, S2, S3, S4);
104   }
105 
Cases(StringLiteral S0,StringLiteral S1,StringLiteral S2,StringLiteral S3,StringLiteral S4,StringLiteral S5,T Value)106   StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2,
107                       StringLiteral S3, StringLiteral S4, StringLiteral S5,
108                       T Value) {
109     return CasesImpl(Value, S0, S1, S2, S3, S4, S5);
110   }
111 
Cases(StringLiteral S0,StringLiteral S1,StringLiteral S2,StringLiteral S3,StringLiteral S4,StringLiteral S5,StringLiteral S6,T Value)112   StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2,
113                       StringLiteral S3, StringLiteral S4, StringLiteral S5,
114                       StringLiteral S6, T Value) {
115     return CasesImpl(Value, S0, S1, S2, S3, S4, S5, S6);
116   }
117 
Cases(StringLiteral S0,StringLiteral S1,StringLiteral S2,StringLiteral S3,StringLiteral S4,StringLiteral S5,StringLiteral S6,StringLiteral S7,T Value)118   StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2,
119                       StringLiteral S3, StringLiteral S4, StringLiteral S5,
120                       StringLiteral S6, StringLiteral S7, T Value) {
121     return CasesImpl(Value, S0, S1, S2, S3, S4, S5, S6, S7);
122   }
123 
Cases(StringLiteral S0,StringLiteral S1,StringLiteral S2,StringLiteral S3,StringLiteral S4,StringLiteral S5,StringLiteral S6,StringLiteral S7,StringLiteral S8,T Value)124   StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2,
125                       StringLiteral S3, StringLiteral S4, StringLiteral S5,
126                       StringLiteral S6, StringLiteral S7, StringLiteral S8,
127                       T Value) {
128     return CasesImpl(Value, S0, S1, S2, S3, S4, S5, S6, S7, S8);
129   }
130 
Cases(StringLiteral S0,StringLiteral S1,StringLiteral S2,StringLiteral S3,StringLiteral S4,StringLiteral S5,StringLiteral S6,StringLiteral S7,StringLiteral S8,StringLiteral S9,T Value)131   StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2,
132                       StringLiteral S3, StringLiteral S4, StringLiteral S5,
133                       StringLiteral S6, StringLiteral S7, StringLiteral S8,
134                       StringLiteral S9, T Value) {
135     return CasesImpl(Value, S0, S1, S2, S3, S4, S5, S6, S7, S8, S9);
136   }
137 
138   // Case-insensitive case matchers.
CaseLower(StringLiteral S,T Value)139   StringSwitch &CaseLower(StringLiteral S, T Value) {
140     CaseLowerImpl(Value, S);
141     return *this;
142   }
143 
EndsWithLower(StringLiteral S,T Value)144   StringSwitch &EndsWithLower(StringLiteral S, T Value) {
145     if (!Result && Str.ends_with_insensitive(S))
146       Result = Value;
147 
148     return *this;
149   }
150 
StartsWithLower(StringLiteral S,T Value)151   StringSwitch &StartsWithLower(StringLiteral S, T Value) {
152     if (!Result && Str.starts_with_insensitive(S))
153       Result = std::move(Value);
154 
155     return *this;
156   }
157 
CasesLower(StringLiteral S0,StringLiteral S1,T Value)158   StringSwitch &CasesLower(StringLiteral S0, StringLiteral S1, T Value) {
159     return CasesLowerImpl(Value, S0, S1);
160   }
161 
CasesLower(StringLiteral S0,StringLiteral S1,StringLiteral S2,T Value)162   StringSwitch &CasesLower(StringLiteral S0, StringLiteral S1, StringLiteral S2,
163                            T Value) {
164     return CasesLowerImpl(Value, S0, S1, S2);
165   }
166 
CasesLower(StringLiteral S0,StringLiteral S1,StringLiteral S2,StringLiteral S3,T Value)167   StringSwitch &CasesLower(StringLiteral S0, StringLiteral S1, StringLiteral S2,
168                            StringLiteral S3, T Value) {
169     return CasesLowerImpl(Value, S0, S1, S2, S3);
170   }
171 
CasesLower(StringLiteral S0,StringLiteral S1,StringLiteral S2,StringLiteral S3,StringLiteral S4,T Value)172   StringSwitch &CasesLower(StringLiteral S0, StringLiteral S1, StringLiteral S2,
173                            StringLiteral S3, StringLiteral S4, T Value) {
174     return CasesLowerImpl(Value, S0, S1, S2, S3, S4);
175   }
176 
Default(T Value)177   [[nodiscard]] R Default(T Value) {
178     if (Result)
179       return std::move(*Result);
180     return Value;
181   }
182 
R()183   [[nodiscard]] operator R() {
184     assert(Result && "Fell off the end of a string-switch");
185     return std::move(*Result);
186   }
187 
188 private:
189   // Returns true when `Str` matches the `S` argument, and stores the result.
CaseImpl(T & Value,StringLiteral S)190   bool CaseImpl(T &Value, StringLiteral S) {
191     if (!Result && Str == S) {
192       Result = std::move(Value);
193       return true;
194     }
195     return false;
196   }
197 
198   // Returns true when `Str` matches the `S` argument (case-insensitive), and
199   // stores the result.
CaseLowerImpl(T & Value,StringLiteral S)200   bool CaseLowerImpl(T &Value, StringLiteral S) {
201     if (!Result && Str.equals_insensitive(S)) {
202       Result = std::move(Value);
203       return true;
204     }
205     return false;
206   }
207 
CasesImpl(T & Value,Args...Cases)208   template <typename... Args> StringSwitch &CasesImpl(T &Value, Args... Cases) {
209     // Stop matching after the string is found.
210     (... || CaseImpl(Value, Cases));
211     return *this;
212   }
213 
214   template <typename... Args>
CasesLowerImpl(T & Value,Args...Cases)215   StringSwitch &CasesLowerImpl(T &Value, Args... Cases) {
216     // Stop matching after the string is found.
217     (... || CaseLowerImpl(Value, Cases));
218     return *this;
219   }
220 };
221 
222 } // end namespace llvm
223 
224 #endif // LLVM_ADT_STRINGSWITCH_H
225