blob: 6c539cb53604c170404e6c4aabd141f79e082ff7 [file] [log] [blame]
James Kuszmaul8e62b022022-03-22 09:33:25 -07001#ifndef FLATBUFFERS_NAMER
2#define FLATBUFFERS_NAMER
3
4#include "flatbuffers/idl.h"
5#include "flatbuffers/util.h"
6
7namespace flatbuffers {
8
9// Options for Namer::File.
10enum class SkipFile {
11 None = 0,
12 Suffix = 1,
13 Extension = 2,
14 SuffixAndExtension = 3,
15};
16inline SkipFile operator&(SkipFile a, SkipFile b) {
17 return static_cast<SkipFile>(static_cast<int>(a) & static_cast<int>(b));
18}
19// Options for Namer::Directories
20enum class SkipDir {
21 None = 0,
22 // Skip prefixing the -o $output_path.
23 OutputPath = 1,
24 // Skip trailing path seperator.
25 TrailingPathSeperator = 2,
26 OutputPathAndTrailingPathSeparator = 3,
27};
28inline SkipDir operator&(SkipDir a, SkipDir b) {
29 return static_cast<SkipDir>(static_cast<int>(a) & static_cast<int>(b));
30}
31
32// `Namer` applies style configuration to symbols in generated code. It manages
33// casing, escapes keywords, and object API naming.
34// TODO: Refactor all code generators to use this.
35class Namer {
36 public:
37 struct Config {
38 // Symbols in code.
39
40 // Case style for flatbuffers-defined types.
41 // e.g. `class TableA {}`
42 Case types;
43 // Case style for flatbuffers-defined constants.
44 // e.g. `uint64_t ENUM_A_MAX`;
45 Case constants;
46 // Case style for flatbuffers-defined methods.
47 // e.g. `class TableA { int field_a(); }`
48 Case methods;
49 // Case style for flatbuffers-defined functions.
50 // e.g. `TableA* get_table_a_root()`;
51 Case functions;
52 // Case style for flatbuffers-defined fields.
53 // e.g. `struct Struct { int my_field; }`
54 Case fields;
55 // Case style for flatbuffers-defined variables.
56 // e.g. `int my_variable = 2`
57 Case variables;
58 // Case style for flatbuffers-defined variants.
59 // e.g. `enum class Enum { MyVariant, }`
60 Case variants;
61 // Seperator for qualified enum names.
62 // e.g. `Enum::MyVariant` uses `::`.
63 std::string enum_variant_seperator;
64
65 // Namespaces
66
67 // e.g. `namespace my_namespace {}`
68 Case namespaces;
69 // The seperator between namespaces in a namespace path.
70 std::string namespace_seperator;
71
72 // Object API.
73 // Native versions flatbuffers types have this prefix.
74 // e.g. "" (it's usually empty string)
75 std::string object_prefix;
76 // Native versions flatbuffers types have this suffix.
77 // e.g. "T"
78 std::string object_suffix;
79
80 // Keywords.
81 // Prefix used to escape keywords. It is usually empty string.
82 std::string keyword_prefix;
83 // Suffix used to escape keywords. It is usually "_".
84 std::string keyword_suffix;
85
86 // Files.
87
88 // Case style for filenames. e.g. `foo_bar_generated.rs`
89 Case filenames;
90 // Case style for directories, e.g. `output_files/foo_bar/baz/`
91 Case directories;
92 // The directory within which we will generate files.
93 std::string output_path;
94 // Suffix for generated file names, e.g. "_generated".
95 std::string filename_suffix;
96 // Extension for generated files, e.g. ".cpp" or ".rs".
97 std::string filename_extension;
98
99 // This is a temporary helper function for code generators to call until all
100 // code generators are using `Namer`. After that point, we can centralize
101 // flag-overriding logic into flatc.cpp
102 Config WithFlagOptions(const IDLOptions &opts,
103 const std::string &path) const {
104 Config result = *this;
105 result.object_prefix = opts.object_prefix;
106 result.object_suffix = opts.object_suffix;
107 result.output_path = path;
108 result.filename_suffix = opts.filename_suffix;
109 return result;
110 }
111 };
112 Namer(Config config, std::set<std::string> keywords)
113 : config_(config), keywords_(std::move(keywords)) {}
114
115 std::string Type(const std::string &s) const {
116 return Format(s, config_.types);
117 }
118
119 std::string Method(const std::string &s) const {
120 return Format(s, config_.methods);
121 }
122
123 std::string Constant(const std::string &s) const {
124 return Format(s, config_.constants);
125 }
126
127 std::string Function(const std::string &s) const {
128 return Format(s, config_.functions);
129 }
130
131 std::string Field(const std::string &s) const {
132 return Format(s, config_.fields);
133 }
134
135 std::string Variable(const std::string &s) const {
136 return Format(s, config_.variables);
137 }
138
139 std::string Variant(const std::string &s) const {
140 return Format(s, config_.variants);
141 }
142
143 std::string EnumVariant(const std::string &e, const std::string v) const {
144 return Type(e) + config_.enum_variant_seperator + Variant(v);
145 }
146
147 std::string ObjectType(const std::string &s) const {
148 return config_.object_prefix + Type(s) + config_.object_suffix;
149 }
150
151 std::string Namespace(const std::string &s) const {
152 return Format(s, config_.namespaces);
153 }
154
155 std::string Namespace(const std::vector<std::string> &ns) const {
156 std::string result;
157 for (auto it = ns.begin(); it != ns.end(); it++) {
158 if (it != ns.begin()) result += config_.namespace_seperator;
159 result += Namespace(*it);
160 }
161 return result;
162 }
163
164 std::string NamespacedType(const std::vector<std::string> &ns,
165 const std::string &s) const {
166 return Namespace(ns) + config_.namespace_seperator + Type(s);
167 }
168
169 // Returns `filename` with the right casing, suffix, and extension.
170 std::string File(const std::string &filename,
171 SkipFile skips = SkipFile::None) const {
172 const bool skip_suffix = (skips & SkipFile::Suffix) != SkipFile::None;
173 const bool skip_ext = (skips & SkipFile::Extension) != SkipFile::None;
174 return ConvertCase(filename, config_.filenames, Case::kUpperCamel) +
175 (skip_suffix ? "" : config_.filename_suffix) +
176 (skip_ext ? "" : config_.filename_extension);
177 }
178 // Formats `directories` prefixed with the output_path and joined with the
179 // right seperator. Output path prefixing and the trailing separator may be
180 // skiped using `skips`.
181 // Callers may want to use `EnsureDirExists` with the result.
182 std::string Directories(const std::vector<std::string> &directories,
183 SkipDir skips = SkipDir::None) const {
184 const bool skip_output_path =
185 (skips & SkipDir::OutputPath) != SkipDir::None;
186 const bool skip_trailing_seperator =
187 (skips & SkipDir::TrailingPathSeperator) != SkipDir::None;
188 std::string result = skip_output_path ? "" : config_.output_path;
189 for (auto d = directories.begin(); d != directories.end(); d++) {
190 result += ConvertCase(*d, config_.directories, Case::kUpperCamel);
191 result.push_back(kPathSeparator);
192 }
193 if (skip_trailing_seperator) result.pop_back();
194 return result;
195 }
196
197 std::string EscapeKeyword(const std::string &name) const {
198 if (keywords_.find(name) == keywords_.end()) {
199 return name;
200 } else {
201 return config_.keyword_prefix + name + config_.keyword_suffix;
202 }
203 }
204
205 private:
206 std::string Format(const std::string &s, Case casing) const {
207 // NOTE: If you need to escape keywords after converting case, which would
208 // make more sense than this, make it a config option.
209 return ConvertCase(EscapeKeyword(s), casing, Case::kLowerCamel);
210 }
211 const Config config_;
212 const std::set<std::string> keywords_;
213};
214
215} // namespace flatbuffers
216
217#endif // FLATBUFFERS_NAMER