Github user apocolipse commented on a diff in the pull request: https://github.com/apache/thrift/pull/1084#discussion_r80365498 --- Diff: compiler/cpp/src/generate/t_swift_3_generator.cc --- @@ -0,0 +1,2458 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include <string> +#include <fstream> +#include <iostream> +#include <vector> +#include <set> + +#include <stdlib.h> +#include <sys/stat.h> +#include <sstream> +#include "t_oop_generator.h" +#include "platform.h" + +using std::map; +using std::ostream; +using std::ofstream; +using std::ostringstream; +using std::set; +using std::string; +using std::stringstream; +using std::vector; + +static const string endl = "\n"; // avoid ostream << std::endl flushes + +/** + * Swift 3 code generator. + * + * Designed from the Swift/Cocoa code generator(s) + */ +class t_swift_3_generator : public t_oop_generator { +public: + t_swift_3_generator(t_program* program, + const map<string, string>& parsed_options, + const string& option_string) + : t_oop_generator(program) { + (void)option_string; + map<string, string>::const_iterator iter; + + log_unexpected_ = false; + async_clients_ = false; + debug_descriptions_ = false; + no_strict_ = false; + + for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { + if( iter->first.compare("log_unexpected") == 0) { + log_unexpected_ = true; + } else if( iter->first.compare("async_clients") == 0) { + async_clients_ = true; + } else if( iter->first.compare("no_strict") == 0) { + no_strict_ = true; + } else if( iter->first.compare("debug_descriptions") == 0) { + debug_descriptions_ = true; + } else { + throw "unknown option swift:" + iter->first; + } + } + + out_dir_base_ = "gen-swift"; + } + + /** + * Init and close methods + */ + + void init_generator(); + void close_generator(); + + void generate_consts(vector<t_const*> consts); + + /** + * Program-level generation functions + */ + + void generate_typedef(t_typedef* ttypedef); + void generate_enum(t_enum* tenum); + void generate_struct(t_struct* tstruct); + void generate_xception(t_struct* txception); + void generate_service(t_service* tservice); + + void print_const_value(ostream& out, + string name, + t_type* type, + t_const_value* value, + bool defval = false, + bool is_property = false); + void render_const_value(ostream& out, + t_type* type, + t_const_value* value); + + void generate_swift_struct(ofstream& out, + t_struct* tstruct, + bool is_private, + bool is_result); + void generate_swift_struct_init(ofstream& out, + t_struct* tstruct, + bool all, + bool is_private); + + void generate_swift_struct_implementation(ofstream& out, + t_struct* tstruct, + bool is_result, + bool is_private); + void generate_swift_struct_hashable_extension(ofstream& out, + t_struct* tstruct, + bool is_private); + void generate_swift_struct_equatable_extension(ofstream& out, + t_struct* tstruct, + bool is_private); + void generate_swift_struct_thrift_extension(ofstream& out, + t_struct* tstruct, + bool is_result, + bool is_private); + void generate_swift_struct_reader(ofstream& out, t_struct* tstruct, bool is_private); + void generate_swift_union_reader(ofstream& out, t_struct* tstruct); + + void generate_swift_struct_writer(ofstream& out,t_struct* tstruct, bool is_private); + void generate_swift_struct_result_writer(ofstream& out, t_struct* tstruct); + void generate_swift_struct_printable_extension(ofstream& out, t_struct* tstruct); + + string function_result_helper_struct_type(t_service *tservice, t_function* tfunction); + string function_args_helper_struct_type(t_service* tservice, t_function* tfunction); + void generate_function_helpers(t_service *tservice, t_function* tfunction); + + /** + * Service-level generation functions + */ + + void generate_swift_service_protocol(ofstream& out, t_service* tservice); + void generate_swift_service_protocol_async(ofstream& out, t_service* tservice); + + void generate_swift_service_client(ofstream& out, t_service* tservice); + void generate_swift_service_client_async(ofstream& out, t_service* tservice); + + void generate_swift_service_client_send_function_implementation(ofstream& out, + t_service* tservice, + t_function* tfunction, + bool needs_protocol); + void generate_swift_service_client_send_function_invocation(ofstream& out, t_function* tfunction); + void generate_swift_service_client_send_async_function_invocation(ofstream& out, + t_function* tfunction); + void generate_swift_service_client_recv_function_implementation(ofstream& out, + t_service* tservice, + t_function* tfunction, + bool needs_protocol); + void generate_swift_service_client_implementation(ofstream& out, t_service* tservice); + void generate_swift_service_client_async_implementation(ofstream& out, t_service* tservice); + + void generate_swift_service_server(ofstream& out, t_service* tservice); + void generate_swift_service_server_implementation(ofstream& out, t_service* tservice); + void generate_swift_service_helpers(t_service* tservice); + /** + * Helper rendering functions + */ + + string swift_imports(); + string swift_thrift_imports(); + string type_name(t_type* ttype, bool is_optional=false, bool is_forced=false); + string base_type_name(t_base_type* tbase); + string declare_property(t_field* tfield, bool is_private); + string function_signature(t_function* tfunction); + string async_function_signature(t_function* tfunction); + string argument_list(t_struct* tstruct, string protocol_name, bool is_internal, bool default_val); + string type_to_enum(t_type* ttype, bool qualified=false); + string maybe_escape_identifier(const string& identifier); + string enum_case_name(t_enum_value* tenum_case, bool declaration); + string enum_const_name(string enum_identifier); + void populate_reserved_words(); + void generate_docstring(ofstream& out, string& doc); + +private: + + void block_open(ostream& out) { + out << " {" << endl; + indent_up(); + } + + void block_close(ostream& out, bool end_line=true) { + indent_down(); + indent(out) << "}"; + if (end_line) out << endl; + } + + + bool field_is_optional(t_field* tfield) { + return tfield->get_req() == t_field::T_OPTIONAL; + } + + bool struct_has_required_fields(t_struct* tstruct) { + const vector<t_field*>& members = tstruct->get_members(); + vector<t_field*>::const_iterator m_iter; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + if (!field_is_optional(*m_iter)) { + return true; + } + } + return false; + } + + bool struct_has_optional_fields(t_struct* tstruct) { + const vector<t_field*>& members = tstruct->get_members(); + vector<t_field*>::const_iterator m_iter; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + if (field_is_optional(*m_iter)) { + return true; + } + } + return false; + } + + string constants_declarations_; + + /** + * File streams + */ + + ofstream f_decl_; + ofstream f_impl_; + + bool log_unexpected_; + bool async_clients_; + bool debug_descriptions_; + bool no_strict_; + + set<string> swift_reserved_words_; +}; + +/** + * Prepares for file generation by opening up the necessary file output + * streams. + */ +void t_swift_3_generator::init_generator() { + // Make output directory + MKDIR(get_out_dir().c_str()); + + populate_reserved_words(); + + // we have a .swift declarations file... + string f_decl_name = capitalize(program_name_) + ".swift"; + string f_decl_fullname = get_out_dir() + f_decl_name; + f_decl_.open(f_decl_fullname.c_str()); + + f_decl_ << autogen_comment() << endl; + + f_decl_ << swift_imports() << swift_thrift_imports() << endl; + + // ...and a .swift implementation extensions file + string f_impl_name = capitalize(program_name_) + "+Exts.swift"; + string f_impl_fullname = get_out_dir() + f_impl_name; + f_impl_.open(f_impl_fullname.c_str()); + + f_impl_ << autogen_comment() << endl; + + f_impl_ << swift_imports() << swift_thrift_imports() << endl; + +} + +/** + * Prints the value of a constant with the given type. Note that type checking + * is NOT performed in this function as it is always run beforehand using the + * validate_types method in main.cc + */ +void t_swift_3_generator::print_const_value(ostream& out, + string name, + t_type* type, + t_const_value* value, + bool defval, + bool is_property) { + // type = get_true_type(type); + + // if (type->is_base_type()) { + // string v2 = render_const_value(out, type, value); + // indent(out); + // if (defval) + // out << type_name(type) << " "; + // out << name << " = " << v2 << ";" << endl << endl; + // } else if (type->is_enum()) { + // indent(out); + // if (defval) + // out << type_name(type) << " "; + // out << name << " = " << render_const_value(out, type, value) << ";" << endl << endl; + // } else if (type->is_struct() || type->is_xception()) { + // indent(out); + // const vector<t_field*>& fields = ((t_struct*)type)->get_members(); + // vector<t_field*>::const_iterator f_iter; + // const map<t_const_value*, t_const_value*>& val = value->get_map(); + // map<t_const_value*, t_const_value*>::const_iterator v_iter; + // if (defval) + // out << type_name(type) << " "; + // out << name << " = [" << type_name(type, true) << " new];" + // << endl; + // for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + // t_type* field_type = NULL; + // for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + // if ((*f_iter)->get_name() == v_iter->first->get_string()) { + // field_type = (*f_iter)->get_type(); + // } + // } + // if (field_type == NULL) { + // throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string(); + // } + // string val = render_const_value(out, field_type, v_iter->second); + // std::string cap_name = capitalize(v_iter->first->get_string()); + // indent(out) << "[" << name << " set" << cap_name << ":" << val << "];" << endl; + // } + // } else if (type->is_map()) { + // ostringstream mapout; + // indent(mapout); + // t_type* ktype = ((t_map*)type)->get_key_type(); + // t_type* vtype = ((t_map*)type)->get_val_type(); + // const map<t_const_value*, t_const_value*>& val = value->get_map(); + // map<t_const_value*, t_const_value*>::const_iterator v_iter; + // if (defval) + // mapout << type_name(type) << " "; + // mapout << name << " = @{"; + // for (v_iter = val.begin(); v_iter != val.end();) { + // mapout << render_const_value(out, ktype, v_iter->first, true) << ": " + // << render_const_value(out, vtype, v_iter->second, true); + // if (++v_iter != val.end()) { + // mapout << ", "; + // } + // } + // mapout << "}"; + // out << mapout.str(); + // } else if (type->is_list()) { + // ostringstream listout; + // indent(listout); + // t_type* etype = ((t_list*)type)->get_elem_type(); + // const vector<t_const_value*>& val = value->get_list(); + // vector<t_const_value*>::const_iterator v_iter; + // if (defval) + // listout << type_name(type) << " "; + // listout << name << " = @["; + // for (v_iter = val.begin(); v_iter != val.end();) { + // listout << render_const_value(out, etype, *v_iter, true); + // if (++v_iter != val.end()) { + // listout << ", "; + // } + // } + // listout << "]"; + // out << listout.str(); + // } else if (type->is_set()) { + // ostringstream setout; + // indent(setout); + // t_type* etype = ((t_set*)type)->get_elem_type(); + // const vector<t_const_value*>& val = value->get_list(); + // vector<t_const_value*>::const_iterator v_iter; + // if (defval) + // setout << type_name(type) << " "; + // setout << name << " = [NSSet setWithArray:@["; + // for (v_iter = val.begin(); v_iter != val.end();) { + // setout << render_const_value(out, etype, *v_iter, true); + // if (++v_iter != val.end()) { + // setout << ", "; + // } + // } + // setout << "]]"; + // out << setout.str(); + // } else { + // throw "compiler error: no const of type " + type->get_name(); + // } +} + +/** + * Prints standard Cocoa imports + * + * @return List of imports for Cocoa libraries + */ +string t_swift_3_generator::swift_imports() { + + vector<string> includes_list; + includes_list.push_back("Foundation"); + + ostringstream includes; + + vector<string>::const_iterator i_iter; + for (i_iter=includes_list.begin(); i_iter!=includes_list.end(); ++i_iter) { + includes << "import " << *i_iter << endl; + } + + includes << endl; + + return includes.str(); +} + +/** + * Prints Thrift runtime imports + * + * @return List of imports necessary for Thrift runtime + */ +string t_swift_3_generator::swift_thrift_imports() { + + vector<string> includes_list; + includes_list.push_back("Thrift"); + + ostringstream includes; + + vector<string>::const_iterator i_iter; + for (i_iter=includes_list.begin(); i_iter!=includes_list.end(); ++i_iter) { + includes << "import " << *i_iter << endl; + } + + includes << endl; + + return includes.str(); +} + +/** + * Finish up generation. + */ +void t_swift_3_generator::close_generator() { + // stick our constants declarations at the end of the header file + // since they refer to things we are defining. + f_decl_ << constants_declarations_ << endl; +} + +/** + * Generates a typedef. This is just a simple 1-liner in Swift + * + * @param ttypedef The type definition + */ +void t_swift_3_generator::generate_typedef(t_typedef* ttypedef) { + f_decl_ << indent() << "public typealias " << ttypedef->get_symbolic() + << " = " << type_name(ttypedef->get_type()) << endl; + f_decl_ << endl; +} + + +/** + * Generates code for an enumerated type. In Swift, this is + * essentially the same as the thrift definition itself, using + * Swift syntax. Conforms to TEnum which + * implementes read/write. + * + * @param tenum The enumeration + */ +void t_swift_3_generator::generate_enum(t_enum* tenum) { + f_decl_ << indent() << "public enum " << tenum->get_name() << " : Int32, TEnum"; + block_open(f_decl_); + + vector<t_enum_value*> constants = tenum->get_constants(); + vector<t_enum_value*>::iterator c_iter; + + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + f_decl_ << indent() << "case " << enum_case_name((*c_iter), true) + << " = " << (*c_iter)->get_value() << endl; + } + + f_decl_ << endl; + f_decl_ << indent() << "public init()"; + block_open(f_decl_); + + f_decl_ << indent() << "self = ." << enum_case_name(constants.front(), false) << endl; + block_close(f_decl_); + + block_close(f_decl_); + f_decl_ << endl; +} + +string t_swift_3_generator::enum_case_name(t_enum_value* tenum_case, bool declaration) { + string name = tenum_case->get_name(); + // force to lowercase for Swift style, maybe escape if its a keyword + std::transform(name.begin(), name.end(), name.begin(), ::tolower); + if (declaration) { + name = maybe_escape_identifier(name); + } + return name; +} + +/** + * Renders a constant enum value by transforming the value portion to lowercase + * for Swift style. + */ +string t_swift_3_generator::enum_const_name(string enum_identifier) { + string::iterator it; + for (it = enum_identifier.begin(); it < enum_identifier.end(); ++it) { + if ((*it) == '.') { + break; + } + } + std::transform(it, enum_identifier.end(), it, ::tolower); + return enum_identifier; +} + +/** + * Generates public constants for all Thrift constants. + * + * @param consts Constants to generate + */ +void t_swift_3_generator::generate_consts(vector<t_const*> consts) { + + ostringstream const_interface; + + // Public constants for base types & strings + vector<t_const*>::iterator c_iter; + for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { + t_type* type = (*c_iter)->get_type(); + const_interface << "public let " << capitalize((*c_iter)->get_name()) << " : " << type_name(type) << " = "; + render_const_value(const_interface, type, (*c_iter)->get_value()); + const_interface << endl << endl; + } + + // this gets spit into the header file in ::close_generator + constants_declarations_ = const_interface.str(); + +} + +/** + * Generates a struct definition for a thrift data type. This is a struct + * with public members. Optional types are used for optional properties to + * allow them to be tested for availability. Separate inits are included for + * required properties & all properties. + * + * Generates extensions to provide conformance to TStruct, TSerializable, + * Hashable & Equatable + * + * @param tstruct The struct definition + */ +void t_swift_3_generator::generate_struct(t_struct* tstruct) { + generate_swift_struct(f_decl_, tstruct, false, false); + generate_swift_struct_implementation(f_impl_, tstruct, false, false); +} + +/** + * Exceptions are structs, but they conform to Error + * + * @param tstruct The struct definition + */ +void t_swift_3_generator::generate_xception(t_struct* txception) { + generate_swift_struct(f_decl_, txception, false, false); + generate_swift_struct_implementation(f_impl_, txception, false, false); +} + +void t_swift_3_generator::generate_docstring(ofstream& out, + string& doc) { + if (doc != "") { + std::vector<std::string> strings; + + std::string::size_type pos = 0; + std::string::size_type prev = 0; + while ((pos = doc.find("\n", prev)) != std::string::npos) + { + strings.push_back(doc.substr(prev, pos - prev)); + prev = pos + 1; + } + + // To get the last substring (or only, if delimiter is not found) + strings.push_back(doc.substr(prev)); + + vector<string>::const_iterator d_iter; + for (d_iter = strings.begin(); d_iter != strings.end(); ++d_iter) { + out << indent() << "/// " << (*d_iter) << endl; + } + } +} + + + +/** + * Generate the interface for a struct. Only properties and + * init methods are included. + * + * @param tstruct The struct definition + * @param is_private + * Is the struct public or private + */ +void t_swift_3_generator::generate_swift_struct(ofstream& out, + t_struct* tstruct, + bool is_private, + bool is_result) { + + + string doc = tstruct->get_doc(); + if (doc != "") { + std::vector<std::string> strings; + + std::string::size_type pos = 0; + std::string::size_type prev = 0; + while ((pos = doc.find("\n", prev)) != std::string::npos) + { + strings.push_back(doc.substr(prev, pos - prev)); + prev = pos + 1; + } + + // To get the last substring (or only, if delimiter is not found) + strings.push_back(doc.substr(prev)); + + vector<string>::const_iterator d_iter; + for (d_iter = strings.begin(); d_iter != strings.end(); ++d_iter) { + out << "/// " << (*d_iter) << endl; + } + } + + // properties + const vector<t_field*>& members = tstruct->get_members(); + vector<t_field*>::const_iterator m_iter; + + + if (tstruct->is_union()) { + // special, unions + out << indent() << "public enum " << tstruct->get_name(); + block_open(out); + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + out << endl; + // TODO: Defaults + // add swift docstring if available + string doc = (*m_iter)->get_doc(); + if (doc != "") { + out << indent() << "/// " << doc; + } + out << indent() << "case " + << maybe_escape_identifier((*m_iter)->get_name()) << "(val: " + << type_name((*m_iter)->get_type(), false) << ")" << endl; + } + } else { + // Normal structs + + string visibility = is_private ? "fileprivate" : "public"; + + out << indent() << visibility << " final class " << tstruct->get_name(); --- End diff -- Thrift allows for self-referring structs, Swift does not. Making them final gives the same non-subclassable semantics while allowing to be self-referencing. Just loses value semantics.
--- If your project is set up for it, you can reply to this email and have your reply appear on GitHub as well. If your project does not have this feature enabled and wishes so, or if the feature is enabled but not working, please contact infrastructure at infrastruct...@apache.org or file a JIRA ticket with INFRA. ---