• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #ifndef FLATBUFFERS_INCLUDE_CODEGEN_NAMER_H_
2 #define FLATBUFFERS_INCLUDE_CODEGEN_NAMER_H_
3 
4 #include "flatbuffers/util.h"
5 
6 namespace flatbuffers {
7 
8 // Options for Namer::File.
9 enum class SkipFile {
10   None = 0,
11   Suffix = 1,
12   Extension = 2,
13   SuffixAndExtension = 3,
14 };
15 inline SkipFile operator&(SkipFile a, SkipFile b) {
16   return static_cast<SkipFile>(static_cast<int>(a) & static_cast<int>(b));
17 }
18 // Options for Namer::Directories
19 enum class SkipDir {
20   None = 0,
21   // Skip prefixing the -o $output_path.
22   OutputPath = 1,
23   // Skip trailing path seperator.
24   TrailingPathSeperator = 2,
25   OutputPathAndTrailingPathSeparator = 3,
26 };
27 inline SkipDir operator&(SkipDir a, SkipDir b) {
28   return static_cast<SkipDir>(static_cast<int>(a) & static_cast<int>(b));
29 }
30 
31 // `Namer` applies style configuration to symbols in generated code. It manages
32 // casing, escapes keywords, and object API naming.
33 // TODO: Refactor all code generators to use this.
34 class Namer {
35  public:
36   struct Config {
37     // Symbols in code.
38 
39     // Case style for flatbuffers-defined types.
40     // e.g. `class TableA {}`
41     Case types;
42     // Case style for flatbuffers-defined constants.
43     // e.g. `uint64_t ENUM_A_MAX`;
44     Case constants;
45     // Case style for flatbuffers-defined methods.
46     // e.g. `class TableA { int field_a(); }`
47     Case methods;
48     // Case style for flatbuffers-defined functions.
49     // e.g. `TableA* get_table_a_root()`;
50     Case functions;
51     // Case style for flatbuffers-defined fields.
52     // e.g. `struct Struct { int my_field; }`
53     Case fields;
54     // Case style for flatbuffers-defined variables.
55     // e.g. `int my_variable = 2`
56     Case variables;
57     // Case style for flatbuffers-defined variants.
58     // e.g. `enum class Enum { MyVariant, }`
59     Case variants;
60     // Seperator for qualified enum names.
61     // e.g. `Enum::MyVariant` uses `::`.
62     std::string enum_variant_seperator;
63 
64     // Configures, when formatting code, whether symbols are checked against
65     // keywords and escaped before or after case conversion. It does not make
66     // sense to do so before, but its legacy behavior. :shrug:
67     // TODO(caspern): Deprecate.
68     enum class Escape {
69       BeforeConvertingCase,
70       AfterConvertingCase,
71     };
72     Escape escape_keywords;
73 
74     // Namespaces
75 
76     // e.g. `namespace my_namespace {}`
77     Case namespaces;
78     // The seperator between namespaces in a namespace path.
79     std::string namespace_seperator;
80 
81     // Object API.
82     // Native versions flatbuffers types have this prefix.
83     // e.g. "" (it's usually empty string)
84     std::string object_prefix;
85     // Native versions flatbuffers types have this suffix.
86     // e.g. "T"
87     std::string object_suffix;
88 
89     // Keywords.
90     // Prefix used to escape keywords. It is usually empty string.
91     std::string keyword_prefix;
92     // Suffix used to escape keywords. It is usually "_".
93     std::string keyword_suffix;
94 
95     // Files.
96 
97     // Case style for filenames. e.g. `foo_bar_generated.rs`
98     Case filenames;
99     // Case style for directories, e.g. `output_files/foo_bar/baz/`
100     Case directories;
101     // The directory within which we will generate files.
102     std::string output_path;
103     // Suffix for generated file names, e.g. "_generated".
104     std::string filename_suffix;
105     // Extension for generated files, e.g. ".cpp" or ".rs".
106     std::string filename_extension;
107   };
Namer(Config config,std::set<std::string> keywords)108   Namer(Config config, std::set<std::string> keywords)
109       : config_(config), keywords_(std::move(keywords)) {}
110 
~Namer()111   virtual ~Namer() {}
112 
Method(const T & s)113   template<typename T> std::string Method(const T &s) const {
114     return Method(s.name);
115   }
116 
Method(const std::string & pre,const std::string & mid,const std::string & suf)117   virtual std::string Method(const std::string &pre,
118                              const std::string &mid,
119                              const std::string &suf) const {
120     return Format(pre + "_" +  mid + "_" + suf, config_.methods);
121   }
Method(const std::string & pre,const std::string & suf)122   virtual std::string Method(const std::string &pre,
123                              const std::string &suf) const {
124     return Format(pre + "_" + suf, config_.methods);
125   }
Method(const std::string & s)126   virtual std::string Method(const std::string &s) const {
127     return Format(s, config_.methods);
128   }
129 
Constant(const std::string & s)130   virtual std::string Constant(const std::string &s) const {
131     return Format(s, config_.constants);
132   }
133 
Function(const std::string & s)134   virtual std::string Function(const std::string &s) const {
135     return Format(s, config_.functions);
136   }
137 
Variable(const std::string & s)138   virtual std::string Variable(const std::string &s) const {
139     return Format(s, config_.variables);
140   }
141 
142   template<typename T>
Variable(const std::string & p,const T & s)143   std::string Variable(const std::string &p, const T &s) const {
144     return Format(p + "_" + s.name, config_.variables);
145   }
Variable(const std::string & p,const std::string & s)146   virtual std::string Variable(const std::string &p,
147                                const std::string &s) const {
148     return Format(p + "_" + s, config_.variables);
149   }
150 
Namespace(const std::string & s)151   virtual std::string Namespace(const std::string &s) const {
152     return Format(s, config_.namespaces);
153   }
154 
Namespace(const std::vector<std::string> & ns)155   virtual 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 
NamespacedType(const std::vector<std::string> & ns,const std::string & s)164   virtual std::string NamespacedType(const std::vector<std::string> &ns,
165                                      const std::string &s) const {
166     return (ns.empty() ? "" : (Namespace(ns) + config_.namespace_seperator)) +
167            Type(s);
168   }
169 
170   // Returns `filename` with the right casing, suffix, and extension.
171   virtual std::string File(const std::string &filename,
172                            SkipFile skips = SkipFile::None) const {
173     const bool skip_suffix = (skips & SkipFile::Suffix) != SkipFile::None;
174     const bool skip_ext = (skips & SkipFile::Extension) != SkipFile::None;
175     return ConvertCase(filename, config_.filenames, Case::kUpperCamel) +
176            (skip_suffix ? "" : config_.filename_suffix) +
177            (skip_ext ? "" : config_.filename_extension);
178   }
179   template<typename T>
180   std::string File(const T &f, SkipFile skips = SkipFile::None) const {
181     return File(f.name, skips);
182   }
183 
184   // Formats `directories` prefixed with the output_path and joined with the
185   // right seperator. Output path prefixing and the trailing separator may be
186   // skiped using `skips`.
187   // Callers may want to use `EnsureDirExists` with the result.
188   // input_case is used to tell how to modify namespace. e.g. kUpperCamel will
189   // add a underscode between case changes, so MyGame turns into My_Game
190   // (depending also on the output_case).
191   virtual std::string Directories(const std::vector<std::string> &directories,
192                                   SkipDir skips = SkipDir::None,
193                                   Case input_case = Case::kUpperCamel) const {
194     const bool skip_output_path =
195         (skips & SkipDir::OutputPath) != SkipDir::None;
196     const bool skip_trailing_seperator =
197         (skips & SkipDir::TrailingPathSeperator) != SkipDir::None;
198     std::string result = skip_output_path ? "" : config_.output_path;
199     for (auto d = directories.begin(); d != directories.end(); d++) {
200       result += ConvertCase(*d, config_.directories, input_case);
201       result.push_back(kPathSeparator);
202     }
203     if (skip_trailing_seperator && !result.empty()) result.pop_back();
204     return result;
205   }
206 
EscapeKeyword(const std::string & name)207   virtual std::string EscapeKeyword(const std::string &name) const {
208     if (keywords_.find(name) == keywords_.end()) {
209       return name;
210     } else {
211       return config_.keyword_prefix + name + config_.keyword_suffix;
212     }
213   }
214 
Type(const std::string & s)215   virtual std::string Type(const std::string &s) const {
216     return Format(s, config_.types);
217   }
Type(const std::string & t,const std::string & s)218   virtual std::string Type(const std::string &t, const std::string &s) const {
219     return Format(t + "_" + s, config_.types);
220   }
221 
ObjectType(const std::string & s)222   virtual std::string ObjectType(const std::string &s) const {
223     return config_.object_prefix + Type(s) + config_.object_suffix;
224   }
225 
Field(const std::string & s)226   virtual std::string Field(const std::string &s) const {
227     return Format(s, config_.fields);
228   }
229 
Variant(const std::string & s)230   virtual std::string Variant(const std::string &s) const {
231     return Format(s, config_.variants);
232   }
233 
Format(const std::string & s,Case casing)234   virtual std::string Format(const std::string &s, Case casing) const {
235     if (config_.escape_keywords == Config::Escape::BeforeConvertingCase) {
236       return ConvertCase(EscapeKeyword(s), casing, Case::kLowerCamel);
237     } else {
238       return EscapeKeyword(ConvertCase(s, casing, Case::kLowerCamel));
239     }
240   }
241 
242   // Denamespaces a string (e.g. The.Quick.Brown.Fox) by returning the last part
243   // after the `delimiter` (Fox) and placing the rest in `namespace_prefix`
244   // (The.Quick.Brown).
245   virtual std::string Denamespace(const std::string &s,
246                                   std::string &namespace_prefix,
247                                   const char delimiter = '.') const {
248     const size_t pos = s.find_last_of(delimiter);
249     if (pos == std::string::npos) {
250       namespace_prefix = "";
251       return s;
252     }
253     namespace_prefix = s.substr(0, pos);
254     return s.substr(pos + 1);
255   }
256 
257   // Same as above, but disregards the prefix.
258   virtual std::string Denamespace(const std::string &s,
259                                   const char delimiter = '.') const {
260     std::string prefix;
261     return Denamespace(s, prefix, delimiter);
262   }
263 
264   const Config config_;
265   const std::set<std::string> keywords_;
266 };
267 
268 }  // namespace flatbuffers
269 
270 #endif  // FLATBUFFERS_INCLUDE_CODEGEN_NAMER_H_
271