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