1 #ifndef FLATBUFFERS_NAMER 2 #define FLATBUFFERS_NAMER 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 virtual std::string Directories(const std::vector<std::string> &directories, 189 SkipDir skips = SkipDir::None) const { 190 const bool skip_output_path = 191 (skips & SkipDir::OutputPath) != SkipDir::None; 192 const bool skip_trailing_seperator = 193 (skips & SkipDir::TrailingPathSeperator) != SkipDir::None; 194 std::string result = skip_output_path ? "" : config_.output_path; 195 for (auto d = directories.begin(); d != directories.end(); d++) { 196 result += ConvertCase(*d, config_.directories, Case::kUpperCamel); 197 result.push_back(kPathSeparator); 198 } 199 if (skip_trailing_seperator) result.pop_back(); 200 return result; 201 } 202 EscapeKeyword(const std::string & name)203 virtual std::string EscapeKeyword(const std::string &name) const { 204 if (keywords_.find(name) == keywords_.end()) { 205 return name; 206 } else { 207 return config_.keyword_prefix + name + config_.keyword_suffix; 208 } 209 } 210 Type(const std::string & s)211 virtual std::string Type(const std::string &s) const { 212 return Format(s, config_.types); 213 } Type(const std::string & t,const std::string & s)214 virtual std::string Type(const std::string &t, const std::string &s) const { 215 return Format(t + "_" + s, config_.types); 216 } 217 ObjectType(const std::string & s)218 virtual std::string ObjectType(const std::string &s) const { 219 return config_.object_prefix + Type(s) + config_.object_suffix; 220 } 221 Field(const std::string & s)222 virtual std::string Field(const std::string &s) const { 223 return Format(s, config_.fields); 224 } 225 Variant(const std::string & s)226 virtual std::string Variant(const std::string &s) const { 227 return Format(s, config_.variants); 228 } 229 Format(const std::string & s,Case casing)230 virtual std::string Format(const std::string &s, Case casing) const { 231 if (config_.escape_keywords == Config::Escape::BeforeConvertingCase) { 232 return ConvertCase(EscapeKeyword(s), casing, Case::kLowerCamel); 233 } else { 234 return EscapeKeyword(ConvertCase(s, casing, Case::kLowerCamel)); 235 } 236 } 237 238 // Denamespaces a string (e.g. The.Quick.Brown.Fox) by returning the last part 239 // after the `delimiter` (Fox) and placing the rest in `namespace_prefix` 240 // (The.Quick.Brown). 241 virtual std::string Denamespace(const std::string &s, 242 std::string &namespace_prefix, 243 const char delimiter = '.') const { 244 const size_t pos = s.find_last_of(delimiter); 245 if (pos == std::string::npos) { 246 namespace_prefix = ""; 247 return s; 248 } 249 namespace_prefix = s.substr(0, pos); 250 return s.substr(pos + 1); 251 } 252 253 // Same as above, but disregards the prefix. 254 virtual std::string Denamespace(const std::string &s, 255 const char delimiter = '.') const { 256 std::string prefix; 257 return Denamespace(s, prefix, delimiter); 258 } 259 260 const Config config_; 261 const std::set<std::string> keywords_; 262 }; 263 264 } // namespace flatbuffers 265 266 #endif // FLATBUFFERS_NAMER 267