[ https://issues.apache.org/jira/browse/THRIFT-3773?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15518786#comment-15518786 ]
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_r80355496 --- 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(); + + if (tstruct->is_xception()) { + out << " : Swift.Error"; // Error seems to be a common exception name in thrift + } + + 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() << declare_property(*m_iter, is_private) << endl; + } + + out << endl; + + // init TODO: Remove, no need for generic init + + // indent(out) << visibility << " init() { }" << endl; + + out << endl; + if (!struct_has_required_fields(tstruct)) { + indent(out) << visibility << " init() { }" << endl; + } + if (struct_has_required_fields(tstruct)) { + generate_swift_struct_init(out, tstruct, false, is_private); + } + if (struct_has_optional_fields(tstruct)) { + generate_swift_struct_init(out, tstruct, true, is_private); + } + } + + block_close(out); + + out << endl; +} + +/** + * Generate struct init for properties + * + * @param tstruct The structure definition + * @param all Generate init with all or just required properties + * @param is_private + * Is the initializer public or private + */ +void t_swift_3_generator::generate_swift_struct_init(ofstream& out, + t_struct* tstruct, + bool all, + bool is_private) { + + string visibility = is_private ? "fileprivate" : "public"; + + indent(out) << visibility << " init("; + + const vector<t_field*>& members = tstruct->get_members(); + vector<t_field*>::const_iterator m_iter; + + bool first=true; + for (m_iter = members.begin(); m_iter != members.end();) { + if (all || !field_is_optional(*m_iter)) { + if (first) { + first = false; + } + else { + out << ", "; + } + out << (*m_iter)->get_name() << ": " + << maybe_escape_identifier(type_name((*m_iter)->get_type(), field_is_optional(*m_iter))); + } + ++m_iter; + } + out << ")"; + + block_open(out); + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + if (all || (*m_iter)->get_req() == t_field::T_REQUIRED || (*m_iter)->get_req() == t_field::T_OPT_IN_REQ_OUT) { + out << indent() << "self." << maybe_escape_identifier((*m_iter)->get_name()) << " = " + << maybe_escape_identifier((*m_iter)->get_name()) << endl; + } + } + + block_close(out); + + out << endl; +} + +/** + * Generate the hashable protocol implmentation + * + * @param tstruct The structure definition + * @param is_private + * Is the struct public or private + */ +void t_swift_3_generator::generate_swift_struct_hashable_extension(ofstream& out, + t_struct* tstruct, + bool is_private) { + + string visibility = is_private ? "fileprivate" : "public"; + + indent(out) << "extension " << tstruct->get_name() << " : Hashable"; + + block_open(out); + + out << endl; + + indent(out) << visibility << " var hashValue : Int"; + + block_open(out); + + const vector<t_field*>& members = tstruct->get_members(); + vector<t_field*>::const_iterator m_iter; + + if (!members.empty()) { + indent(out) << "let prime = 31" << endl; + indent(out) << "var result = 1" << endl; + if (!tstruct->is_union()) { + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + t_field* tfield = *m_iter; + string accessor = field_is_optional(tfield) ? "?." : "."; + string defaultor = field_is_optional(tfield) ? " ?? 0" : ""; + indent(out) << "result = prime &* result &+ (" << maybe_escape_identifier(tfield->get_name()) << accessor + << "hashValue" << defaultor << ")" << endl; + } + } else { + indent(out) << "switch self {" << endl; + for (m_iter = members.begin(); m_iter != members.end(); m_iter++) { + t_field *tfield = *m_iter; + indent(out) << "case ." << tfield->get_name() << "(let val): result = prime &* val.hashValue" << endl; + } + indent(out) << "}" << endl << endl; + } + + + indent(out) << "return result" << endl; + } + else { + indent(out) << "return 31" << endl; + } + + block_close(out); + + out << endl; + + block_close(out); + + out << endl; +} + +/** + * Generate the equatable protocol implementation + * + * @param tstruct The structure definition + * @param is_private + * Is the struct public or private + */ +void t_swift_3_generator::generate_swift_struct_equatable_extension(ofstream& out, + t_struct* tstruct, + bool is_private) { + + string visibility = is_private ? "fileprivate" : "public"; + + indent(out) << visibility << " func ==(lhs: " << type_name(tstruct) << ", rhs: " << type_name(tstruct) << ") -> Bool"; + + block_open(out); + + indent(out) << "return"; + + const vector<t_field*>& members = tstruct->get_members(); + vector<t_field*>::const_iterator m_iter; + + if (members.size()) { + if (!tstruct->is_union()) { + out << endl; + indent_up(); + + for (m_iter = members.begin(); m_iter != members.end();) { + t_field* tfield = *m_iter; + indent(out) << "(lhs." << maybe_escape_identifier(tfield->get_name()) + << " == rhs." << maybe_escape_identifier(tfield->get_name()) << ")"; + if (++m_iter != members.end()) { + out << " &&"; + } + out << endl; + } + + indent_down(); + } else { + block_open(out); + indent(out) << "switch (lhs, rhs) {" << endl; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + t_field* tfield = *m_iter; + indent(out) << "case (." << tfield->get_name() << "(let lval), ." + << tfield->get_name() << "(let rval)): return lval == rval" + << endl; + } + indent(out) << "default: return false" << endl; + indent(out) << "}" << endl; + indent_down(); + indent(out) << "}()" << endl; + } + + } + else { + out << " true" << endl; + } + + block_close(out); + + out << endl; +} + +/** + * Generate struct implementation. Produces extensions that + * fulfill the requisite protocols to complete the value. + * + * @param tstruct The struct definition + * @param is_result + * If this is a result it needs a different writer + * @param is_private + * Is the struct public or private + */ +void t_swift_3_generator::generate_swift_struct_implementation(ofstream& out, + t_struct* tstruct, + bool is_result, + bool is_private) { + + generate_swift_struct_equatable_extension(out, tstruct, is_private); + + if (!is_private && !is_result) { + generate_swift_struct_printable_extension(out, tstruct); + } + + generate_swift_struct_hashable_extension(out, tstruct, is_private); + generate_swift_struct_thrift_extension(out, tstruct, is_result, is_private); + + out << endl << endl; +} + + +/** + * Generate the TStruct protocol implementation. + * + * @param tstruct The structure definition + * @param is_result + * Is the struct a result value + * @param is_private + * Is the struct public or private + */ +void t_swift_3_generator::generate_swift_struct_thrift_extension(ofstream& out, + t_struct* tstruct, + bool is_result, + bool is_private) { + + indent(out) << "extension " << tstruct->get_name() << " : TStruct"; + + block_open(out); + + out << endl; + + string access = (is_private) ? "fileprivate" : "public"; + // generate fieldID's dictionary + out << indent() << access << " static var fieldIds: [String: Int32]"; + block_open(out); + out << indent() << "return ["; + const vector<t_field*>& fields = tstruct->get_members(); + vector<t_field*>::const_iterator f_iter; + bool wrote = false; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + wrote = true; + out << "\"" << (*f_iter)->get_name() << "\": " << (*f_iter)->get_key() << ", "; + } + if (!wrote) { + // pad a colon + out << ":"; + } + out << "]" << endl; + block_close(out); + out << endl; + out << indent() << access << " static var structName: String { return \"" + << tstruct->get_name() << "\" }" << endl << endl; + + + if (tstruct->is_union()) { + generate_swift_union_reader(out, tstruct); + } else { + generate_swift_struct_reader(out, tstruct, is_private); + } + block_close(out); + + out << endl; +} + +void t_swift_3_generator::generate_swift_union_reader(ofstream& out, + t_struct* tstruct) { + indent(out) << "public static func read(from proto: TProtocol) throws -> " + << tstruct->get_name(); + block_open(out); + indent(out) << "_ = try proto.readStructBegin()" << endl; + + indent(out) << "var ret: " << tstruct->get_name() << "?"; + out << endl; + indent(out) << "fields: while true"; + block_open(out); + out << endl; + indent(out) << "let (_, fieldType, fieldID) = try proto.readFieldBegin()" << endl << endl; + indent(out) << "switch (fieldID, fieldType)"; + block_open(out); + indent(out) << "case (_, .stop): break fields" << endl; + + const vector<t_field*>& fields = tstruct->get_members(); + vector<t_field*>::const_iterator f_iter; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + indent(out) << "case (" << (*f_iter)->get_key() << ", " << type_to_enum((*f_iter)->get_type()) << "):";// << endl; + string padding = ""; + + t_type* type = get_true_type((*f_iter)->get_type()); + if (type->is_base_type()) { + t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); + switch (tbase) { + case t_base_type::TYPE_STRING: + case t_base_type::TYPE_DOUBLE: + padding = " "; + break; + + case t_base_type::TYPE_BOOL: + case t_base_type::TYPE_I8: + padding = " "; + break; + case t_base_type::TYPE_I16: + case t_base_type::TYPE_I32: + case t_base_type::TYPE_I64: + padding = " "; + break; + default: break; + } + } else if (type->is_enum() || type->is_set() || type->is_map()) { + padding = " "; + } else if (type->is_struct() || type->is_xception()) { + padding = " "; + } else if (type->is_list()) { + padding = " "; + } + + // indent_up(); + indent(out) << padding << "ret = " << tstruct->get_name() << "." + << (*f_iter)->get_name() << "(val: " << "try " + << type_name((*f_iter)->get_type(), false, false) + << ".read(from: proto))" << endl; + } + + indent(out) << "case let (_, unknownType): try proto.skip(type: unknownType)" << endl; + + block_close(out); + indent(out) << "try proto.readFieldEnd()" << endl; + + block_close(out); + out << endl; + indent(out) << "if let ret = ret"; + block_open(out); + indent(out) << "return ret" << endl; + block_close(out); + out << endl; + indent(out) << "throw TProtocolError(error: .unknown, message: \"Missing required value for type: " + << tstruct->get_name() << "\")"; + block_close(out); + out << endl; + +} + +/** + * Generates a function to read a struct from + * from a protocol. (TStruct compliance) + * + * @param tstruct The structure definition + * @param is_private + * Is the struct public or private + */ +void t_swift_3_generator::generate_swift_struct_reader(ofstream& out, + t_struct* tstruct, + bool is_private) { + + + string visibility = is_private ? "fileprivate" : "public"; + + indent(out) << visibility << " static func read(from proto: TProtocol) throws -> " + << tstruct->get_name(); + + block_open(out); + + indent(out) << "_ = try proto.readStructBegin()" << endl; + + const vector<t_field*>& fields = tstruct->get_members(); + vector<t_field*>::const_iterator f_iter; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + bool optional = field_is_optional(*f_iter); + indent(out) << "var " << maybe_escape_identifier((*f_iter)->get_name()) << ": " + << type_name((*f_iter)->get_type(), optional, !optional) << endl; + } + + out << endl; + + // Loop over reading in fields + indent(out) << "fields: while true"; + + block_open(out); + + out << endl; + + indent(out) << "let (_, fieldType, fieldID) = try proto.readFieldBegin()" << endl << endl; + indent(out) << "switch (fieldID, fieldType)"; + + block_open(out); + + indent(out) << "case (_, .stop): break fields" << endl; + // indent_up(); + // indent(out) << "break fields" << endl << endl; + // indent_down(); + + // Generate deserialization code for known cases + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + bool optional = field_is_optional(*f_iter); + indent(out) << "case (" << (*f_iter)->get_key() << ", " << type_to_enum((*f_iter)->get_type()) << "):";// << endl; + string padding = ""; + + t_type* type = get_true_type((*f_iter)->get_type()); + if (type->is_base_type()) { + t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); + switch (tbase) { + case t_base_type::TYPE_STRING: + case t_base_type::TYPE_DOUBLE: + padding = " "; + break; + + case t_base_type::TYPE_BOOL: + case t_base_type::TYPE_I8: + padding = " "; + break; + case t_base_type::TYPE_I16: + case t_base_type::TYPE_I32: + case t_base_type::TYPE_I64: + padding = " "; + break; + default: break; + } + } else if (type->is_enum() || type->is_set() || type->is_map()) { + padding = " "; + } else if (type->is_struct() || type->is_xception()) { + padding = " "; + } else if (type->is_list()) { + padding = " "; + } + + // indent_up(); + out << padding << maybe_escape_identifier((*f_iter)->get_name()) << " = try " + << type_name((*f_iter)->get_type(), false, false) << ".read(from: proto)" << endl; + // indent(out) << maybe_escape_identifier((*f_iter)->get_name()) << " = try proto.read() as " + // << type_name((*f_iter)->get_type()) << endl << endl; + // indent_down(); + + } + + indent(out) << "case let (_, unknownType): try proto.skip(type: unknownType)" << endl; + + block_close(out); + + out << endl; + + // Read field end marker + indent(out) << "try proto.readFieldEnd()" << endl; + + block_close(out); + + out << endl; + + indent(out) << "try proto.readStructEnd()" << endl; + + if (struct_has_required_fields(tstruct)) { + // performs various checks (e.g. check that all required fields are set) + indent(out) << "// Required fields" << endl; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (field_is_optional(*f_iter)) { + continue; + } + indent(out) << "try proto.validateValue(" << (*f_iter)->get_name() << ", " + << "named: \"" << (*f_iter)->get_name() << "\")" << endl; + } + } + + out << endl; + + indent(out) << "return " << tstruct->get_name() << "("; + for (f_iter = fields.begin(); f_iter != fields.end();) { + out << (*f_iter)->get_name() << ": " << maybe_escape_identifier((*f_iter)->get_name()); + if (++f_iter != fields.end()) { + out << ", "; + } + } + out << ")" << endl; + + block_close(out); + + out << endl; +} + +/** + * Generates a function to write a struct to + * a protocol. (TStruct compliance) + * + * @param tstruct The structure definition + * @param is_private + * Is the struct public or private + */ +// DEPRECATED: Handled in Library +// void t_swift_3_generator::generate_swift_struct_writer(ofstream& out, +// t_struct* tstruct, +// bool is_private) { + +// string visibility = is_private ? "private" : "public"; + +// indent(out) << visibility << " func write(to proto: TProtocol) throws"; + +// block_open(out); + +// out << endl; + +// string name = tstruct->get_name(); +// const vector<t_field*>& fields = tstruct->get_members(); +// vector<t_field*>::const_iterator f_iter; + +// indent(out) << "try proto.writeStructBegin(name: \"" << name << "\")" << endl; + +// out << endl; + +// for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { +// t_field *tfield = *f_iter; + +// bool optional = field_is_optional(tfield); +// if (optional) { +// indent(out) << "if let " << maybe_escape_identifier(tfield->get_name()) +// << " = __value." << maybe_escape_identifier(tfield->get_name()); +// block_open(out); +// } + +// indent(out) << "try proto.writeFieldValue(" +// << (optional ? "" : "__value.") << maybe_escape_identifier(tfield->get_name()) << ", " +// << "name: \"" << tfield->get_name() << "\", " +// << "type: " << type_to_enum(tfield->get_type()) << ", " +// << "id: " << tfield->get_key() << ")" << endl; + +// if (optional) { +// block_close(out); +// } + +// out << endl; +// } + +// indent(out) << "try __proto.writeFieldStop()" << endl << endl; + +// indent(out) << "try __proto.writeStructEnd()" << endl; + +// block_close(out); + +// out << endl; +// } + +/** + * Generates a function to read a struct from + * from a protocol. (TStruct compliance) + * + * This is specifically a function result. Only + * the first available field is written. + * + * @param tstruct The structure definition + */ +// DEPRECATED: Handled in library +// void t_swift_3_generator::generate_swift_struct_result_writer(ofstream& out, t_struct* tstruct) { + +// indent(out) << "private static func writeValue(__value: " << tstruct->get_name() << ", toProtocol __proto: TProtocol) throws"; + +// block_open(out); + +// out << endl; + +// string name = tstruct->get_name(); +// const vector<t_field*>& fields = tstruct->get_members(); +// vector<t_field*>::const_iterator f_iter; + +// indent(out) << "try __proto.writeStructBeginWithName(\"" << name << "\")" << endl; + +// out << endl; + +// for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { +// t_field *tfield = *f_iter; + +// indent(out) << "if let result = __value." << (*f_iter)->get_name(); + +// block_open(out); + +// indent(out) << "try __proto.writeFieldValue(result, " +// << "name: \"" << tfield->get_name() << "\", " +// << "type: " << type_to_enum(tfield->get_type()) << ", " +// << "id: " << tfield->get_key() << ")" << endl; + +// block_close(out); +// } +// // Write the struct map +// indent(out) << "try __proto.writeFieldStop()" << endl << endl; + +// indent(out) << "try __proto.writeStructEnd()" << endl; + +// block_close(out); + +// out << endl; +// } + +/** + * Generates a description method for the given struct + * + * @param tstruct The struct definition + */ +void t_swift_3_generator::generate_swift_struct_printable_extension(ofstream& out, t_struct* tstruct) { + + // Allow use of debugDescription so the app can add description via a cateogory/extension + + const vector<t_field*>& fields = tstruct->get_members(); + vector<t_field*>::const_iterator f_iter; + + indent(out) << "extension " << tstruct->get_name() << " : " + << (debug_descriptions_ ? "CustomDebugStringConvertible" : "CustomStringConvertible"); --- End diff -- What is the use case for the `debug_descriptions` option? I tested this and it just forces me to implement debugDescription for all the structs instead of being opt-in for the structs I may choose to conform to `CustomDebugStringConvertible`. > 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)