[ https://issues.apache.org/jira/browse/THRIFT-3773?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15518787#comment-15518787 ]
ASF GitHub Bot commented on THRIFT-3773: ---------------------------------------- Github user ChristopherRogers commented on a diff in the pull request: https://github.com/apache/thrift/pull/1084#discussion_r80357106 --- 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 -- I think it would be a more natural choice to use Swift structs for Thrift structs, since they are the same thing conceptually. Did you have any particular reason for choosing reference types instead? There is a performance argument, as reference counting will be more performant if the struct contains lots of other reference types. > Swift Library > ------------- > > Key: THRIFT-3773 > URL: https://issues.apache.org/jira/browse/THRIFT-3773 > Project: Thrift > Issue Type: New Feature > Components: Swift - Library > Reporter: Thomas Bartelmess > > We already have the option to generate Swift code in the Cocoa compiler, > however large parts of the (Objective-C) Cocoa Library still depend on Cocoa > and Objective-C. > It would be good to have a native Swift library that doesn't depend on the > Cocoa libraries. > Design goals: > - Fully compatible with the code that is currently generated by the Cocoa > compiler (both Objective-C and Swift). > - Ability to run on Linux > - Pure Swift, no Objective-C code. > - No dependencies on closed source apple libraries > - Keep the same interface, so that the library is compatible with the code > the current cocoa compiler generates > - Better server support that the current Objective-C library. > - Follow the new Swift packaging format to be compatible with the Swift > Package manager -- This message was sent by Atlassian JIRA (v6.3.4#6332)