[
https://issues.apache.org/jira/browse/THRIFT-3773?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15519366#comment-15519366
]
ASF GitHub Bot commented on THRIFT-3773:
----------------------------------------
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.
> 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)