1198892Srdivacky//===--- StringSwitch.h - Switch-on-literal-string Construct --------------===/
2198892Srdivacky//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6198892Srdivacky//===----------------------------------------------------------------------===/
7198892Srdivacky//
8198892Srdivacky//  This file implements the StringSwitch template, which mimics a switch()
9200581Srdivacky//  statement whose cases are string literals.
10198892Srdivacky//
11198892Srdivacky//===----------------------------------------------------------------------===/
12198892Srdivacky#ifndef LLVM_ADT_STRINGSWITCH_H
13198892Srdivacky#define LLVM_ADT_STRINGSWITCH_H
14198892Srdivacky
15198892Srdivacky#include "llvm/ADT/StringRef.h"
16296417Sdim#include "llvm/Support/Compiler.h"
17198892Srdivacky#include <cassert>
18198892Srdivacky#include <cstring>
19198892Srdivacky
20198892Srdivackynamespace llvm {
21200581Srdivacky
22341825Sdim/// A switch()-like statement whose cases are string literals.
23198892Srdivacky///
24198892Srdivacky/// The StringSwitch class is a simple form of a switch() statement that
25198892Srdivacky/// determines whether the given string matches one of the given string
26198892Srdivacky/// literals. The template type parameter \p T is the type of the value that
27198892Srdivacky/// will be returned from the string-switch expression. For example,
28198892Srdivacky/// the following code switches on the name of a color in \c argv[i]:
29198892Srdivacky///
30198892Srdivacky/// \code
31198892Srdivacky/// Color color = StringSwitch<Color>(argv[i])
32198892Srdivacky///   .Case("red", Red)
33198892Srdivacky///   .Case("orange", Orange)
34198892Srdivacky///   .Case("yellow", Yellow)
35198892Srdivacky///   .Case("green", Green)
36198892Srdivacky///   .Case("blue", Blue)
37198892Srdivacky///   .Case("indigo", Indigo)
38200581Srdivacky///   .Cases("violet", "purple", Violet)
39198892Srdivacky///   .Default(UnknownColor);
40198892Srdivacky/// \endcode
41200581Srdivackytemplate<typename T, typename R = T>
42198892Srdivackyclass StringSwitch {
43341825Sdim  /// The string we are matching.
44341825Sdim  const StringRef Str;
45200581Srdivacky
46341825Sdim  /// The pointer to the result of this switch statement, once known,
47200581Srdivacky  /// null before that.
48341825Sdim  Optional<T> Result;
49200581Srdivacky
50198892Srdivackypublic:
51239462Sdim  explicit StringSwitch(StringRef S)
52341825Sdim  : Str(S), Result() { }
53200581Srdivacky
54314564Sdim  // StringSwitch is not copyable.
55314564Sdim  StringSwitch(const StringSwitch &) = delete;
56341825Sdim
57341825Sdim  // StringSwitch is not assignable due to 'Str' being 'const'.
58314564Sdim  void operator=(const StringSwitch &) = delete;
59341825Sdim  void operator=(StringSwitch &&other) = delete;
60314564Sdim
61341825Sdim  StringSwitch(StringSwitch &&other)
62341825Sdim    : Str(other.Str), Result(std::move(other.Result)) { }
63314564Sdim
64314564Sdim  ~StringSwitch() = default;
65314564Sdim
66314564Sdim  // Case-sensitive case matchers
67341825Sdim  StringSwitch &Case(StringLiteral S, T Value) {
68341825Sdim    if (!Result && Str == S) {
69341825Sdim      Result = std::move(Value);
70198892Srdivacky    }
71198892Srdivacky    return *this;
72198892Srdivacky  }
73200581Srdivacky
74341825Sdim  StringSwitch& EndsWith(StringLiteral S, T Value) {
75341825Sdim    if (!Result && Str.endswith(S)) {
76341825Sdim      Result = std::move(Value);
77212904Sdim    }
78212904Sdim    return *this;
79212904Sdim  }
80212904Sdim
81341825Sdim  StringSwitch& StartsWith(StringLiteral S, T Value) {
82341825Sdim    if (!Result && Str.startswith(S)) {
83341825Sdim      Result = std::move(Value);
84212904Sdim    }
85212904Sdim    return *this;
86212904Sdim  }
87212904Sdim
88341825Sdim  StringSwitch &Cases(StringLiteral S0, StringLiteral S1, T Value) {
89314564Sdim    return Case(S0, Value).Case(S1, Value);
90199481Srdivacky  }
91200581Srdivacky
92341825Sdim  StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2,
93341825Sdim                      T Value) {
94314564Sdim    return Case(S0, Value).Cases(S1, S2, Value);
95199481Srdivacky  }
96200581Srdivacky
97341825Sdim  StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2,
98341825Sdim                      StringLiteral S3, T Value) {
99314564Sdim    return Case(S0, Value).Cases(S1, S2, S3, Value);
100199481Srdivacky  }
101199481Srdivacky
102341825Sdim  StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2,
103341825Sdim                      StringLiteral S3, StringLiteral S4, T Value) {
104314564Sdim    return Case(S0, Value).Cases(S1, S2, S3, S4, Value);
105314564Sdim  }
106314564Sdim
107341825Sdim  StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2,
108341825Sdim                      StringLiteral S3, StringLiteral S4, StringLiteral S5,
109341825Sdim                      T Value) {
110314564Sdim    return Case(S0, Value).Cases(S1, S2, S3, S4, S5, Value);
111314564Sdim  }
112314564Sdim
113341825Sdim  StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2,
114341825Sdim                      StringLiteral S3, StringLiteral S4, StringLiteral S5,
115341825Sdim                      StringLiteral S6, T Value) {
116314564Sdim    return Case(S0, Value).Cases(S1, S2, S3, S4, S5, S6, Value);
117314564Sdim  }
118314564Sdim
119341825Sdim  StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2,
120341825Sdim                      StringLiteral S3, StringLiteral S4, StringLiteral S5,
121341825Sdim                      StringLiteral S6, StringLiteral S7, T Value) {
122314564Sdim    return Case(S0, Value).Cases(S1, S2, S3, S4, S5, S6, S7, Value);
123314564Sdim  }
124314564Sdim
125341825Sdim  StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2,
126341825Sdim                      StringLiteral S3, StringLiteral S4, StringLiteral S5,
127341825Sdim                      StringLiteral S6, StringLiteral S7, StringLiteral S8,
128341825Sdim                      T Value) {
129314564Sdim    return Case(S0, Value).Cases(S1, S2, S3, S4, S5, S6, S7, S8, Value);
130314564Sdim  }
131314564Sdim
132341825Sdim  StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2,
133341825Sdim                      StringLiteral S3, StringLiteral S4, StringLiteral S5,
134341825Sdim                      StringLiteral S6, StringLiteral S7, StringLiteral S8,
135341825Sdim                      StringLiteral S9, T Value) {
136314564Sdim    return Case(S0, Value).Cases(S1, S2, S3, S4, S5, S6, S7, S8, S9, Value);
137314564Sdim  }
138314564Sdim
139314564Sdim  // Case-insensitive case matchers.
140341825Sdim  StringSwitch &CaseLower(StringLiteral S, T Value) {
141341825Sdim    if (!Result && Str.equals_lower(S))
142341825Sdim      Result = std::move(Value);
143296417Sdim
144296417Sdim    return *this;
145199481Srdivacky  }
146200581Srdivacky
147341825Sdim  StringSwitch &EndsWithLower(StringLiteral S, T Value) {
148341825Sdim    if (!Result && Str.endswith_lower(S))
149341825Sdim      Result = Value;
150314564Sdim
151314564Sdim    return *this;
152314564Sdim  }
153314564Sdim
154341825Sdim  StringSwitch &StartsWithLower(StringLiteral S, T Value) {
155341825Sdim    if (!Result && Str.startswith_lower(S))
156341825Sdim      Result = std::move(Value);
157314564Sdim
158314564Sdim    return *this;
159314564Sdim  }
160341825Sdim
161341825Sdim  StringSwitch &CasesLower(StringLiteral S0, StringLiteral S1, T Value) {
162314564Sdim    return CaseLower(S0, Value).CaseLower(S1, Value);
163314564Sdim  }
164314564Sdim
165341825Sdim  StringSwitch &CasesLower(StringLiteral S0, StringLiteral S1, StringLiteral S2,
166341825Sdim                           T Value) {
167314564Sdim    return CaseLower(S0, Value).CasesLower(S1, S2, Value);
168314564Sdim  }
169314564Sdim
170341825Sdim  StringSwitch &CasesLower(StringLiteral S0, StringLiteral S1, StringLiteral S2,
171341825Sdim                           StringLiteral S3, T Value) {
172314564Sdim    return CaseLower(S0, Value).CasesLower(S1, S2, S3, Value);
173314564Sdim  }
174314564Sdim
175341825Sdim  StringSwitch &CasesLower(StringLiteral S0, StringLiteral S1, StringLiteral S2,
176341825Sdim                           StringLiteral S3, StringLiteral S4, T Value) {
177314564Sdim    return CaseLower(S0, Value).CasesLower(S1, S2, S3, S4, Value);
178314564Sdim  }
179314564Sdim
180341825Sdim  LLVM_NODISCARD
181341825Sdim  R Default(T Value) {
182200581Srdivacky    if (Result)
183341825Sdim      return std::move(*Result);
184198892Srdivacky    return Value;
185198892Srdivacky  }
186200581Srdivacky
187341825Sdim  LLVM_NODISCARD
188341825Sdim  operator R() {
189200581Srdivacky    assert(Result && "Fell off the end of a string-switch");
190341825Sdim    return std::move(*Result);
191198892Srdivacky  }
192198892Srdivacky};
193198892Srdivacky
194198892Srdivacky} // end namespace llvm
195198892Srdivacky
196198892Srdivacky#endif // LLVM_ADT_STRINGSWITCH_H
197