diff --git a/CMakeLists.txt b/CMakeLists.txt index 37e89766fd5..b100c7a1123 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,11 +28,12 @@ set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${CMAKE_CURRENT_SOURCE_DIR}/build/ file (STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/configure.ac" CONFIGURE_AC REGEX "AC_INIT\\(.*\\)" ) # The following variable is used in the version.h.in file -string(REGEX REPLACE "AC_INIT\\(\\[.*\\], \\[([0-9]+\\.[0-9]+\\.[0-9]+(-dev)?)\\]\\)" "\\1" PACKAGE_VERSION ${CONFIGURE_AC}) +#string(REGEX REPLACE "AC_INIT\\(\\[.*\\], \\[([0-9]+\\.[0-9]+\\.[0-9]+(-dev)?)\\]\\)" "\\1" PACKAGE_VERSION ${CONFIGURE_AC}) +string(REGEX REPLACE "AC_INIT\\(\\[.*\\], \\[([0-9]+\\.[0-9]+\\.[0-9]+(-dev)?(\\.[a-z0-9-]+)?)\\]\\)" "\\1" PACKAGE_VERSION ${CONFIGURE_AC}) message(STATUS "Parsed Thrift package version: ${PACKAGE_VERSION}") # These are internal to CMake -string(REGEX REPLACE "([0-9]+\\.[0-9]+\\.[0-9]+)(-dev)?" "\\1" thrift_VERSION ${PACKAGE_VERSION}) +string(REGEX REPLACE "([0-9]+\\.[0-9]+\\.[0-9]+)(-dev)?(\\.[a-z0-9-]+)" "\\1" thrift_VERSION ${PACKAGE_VERSION}) string(REGEX REPLACE "([0-9]+)\\.[0-9]+\\.[0-9]+" "\\1" thrift_VERSION_MAJOR ${thrift_VERSION}) string(REGEX REPLACE "[0-9]+\\.([0-9])+\\.[0-9]+" "\\1" thrift_VERSION_MINOR ${thrift_VERSION}) string(REGEX REPLACE "[0-9]+\\.[0-9]+\\.([0-9]+)" "\\1" thrift_VERSION_PATCH ${thrift_VERSION}) diff --git a/compiler/cpp/src/generate/t_swift_generator.cc b/compiler/cpp/src/generate/t_swift_generator.cc index 7d58823e019..5d4f84936b6 100644 --- a/compiler/cpp/src/generate/t_swift_generator.cc +++ b/compiler/cpp/src/generate/t_swift_generator.cc @@ -132,6 +132,14 @@ class t_swift_generator : public t_oop_generator { void generate_swift_struct_result_writer(ofstream& out, t_struct* tstruct); void generate_swift_struct_printable_extension(ofstream& out, t_struct* tstruct); + void generate_swift_union(ofstream& out, t_struct* tstruct); + void generate_swift_union_implementation(ofstream& out, t_struct* tstruct); + void generate_swift_union_equatable_extension(ofstream& out, t_struct* tstruct); + void generate_swift_union_hashable_extension(ofstream& out, t_struct* tstruct); + void generate_swift_union_thrift_extension(ofstream& out, t_struct* tstruct); + void generate_swift_union_reader(ofstream& out, t_struct* tstruct); + void generate_swift_union_writer(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); @@ -420,8 +428,13 @@ void t_swift_generator::generate_consts(vector consts) { * @param tstruct The struct definition */ void t_swift_generator::generate_struct(t_struct* tstruct) { - generate_swift_struct(f_decl_, tstruct, false); - generate_swift_struct_implementation(f_impl_, tstruct, false, false); + if (tstruct->is_union()) { + generate_swift_union(f_decl_, tstruct); + generate_swift_union_implementation(f_impl_, tstruct); + } else { + generate_swift_struct(f_decl_, tstruct, false); + generate_swift_struct_implementation(f_impl_, tstruct, false, false); + } } /** @@ -572,7 +585,7 @@ void t_swift_generator::generate_swift_struct_hashable_extension(ofstream& out, 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 + indent(out) << "result = prime &* result &+ (" << maybe_escape_identifier(tfield->get_name()) << accessor << "hashValue" << defaultor << ")" << endl; } @@ -967,6 +980,315 @@ void t_swift_generator::generate_swift_struct_printable_extension(ofstream& out, out << endl; } +/** + * Generate the definition of a union. + * + * @param tstruct The struct definition + */ +void t_swift_generator::generate_swift_union(ofstream& out, t_struct* tstruct) { + + indent(out) << "public indirect enum " << tstruct->get_name(); + + block_open(out); + + // properties + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + t_field* tfield = *m_iter; + out << endl; + indent(out) << "case " << maybe_escape_identifier(tfield->get_name()) + << "(" << type_name(tfield->get_type()) << ")" << endl; + } + + // Add extra case for unknown values, needed for extensibility + // TODO: could make optional or give configurable name.. + out << endl; + indent(out) << "case ThriftUnknownValue" << endl; + + out << endl; + + // Need default initializer to work with + indent(out) << "public init() { self = .ThriftUnknownValue }" << endl; + + out << endl; + + block_close(out); + + out << endl; +} + +/** + * Generate union implementation. Produces extensions that + * fulfill the requisite protocols to complete the value. + * + * @param tstruct The struct definition + */ +void t_swift_generator::generate_swift_union_implementation(ofstream& out, t_struct* tstruct) { + + generate_swift_union_equatable_extension(out, tstruct); + + generate_swift_union_hashable_extension(out, tstruct); + generate_swift_union_thrift_extension(out, tstruct); + + out << endl << endl; +} + +/** + * Generate the equatable protocol implementation for a union + * + * @param tstruct The structure definition + */ +void t_swift_generator::generate_swift_union_equatable_extension(ofstream& out, t_struct* tstruct) { + + indent(out) << "public func ==(lhs: " << type_name(tstruct) << ", rhs: " << type_name(tstruct) << ") -> Bool"; + + block_open(out); + + indent(out) << "switch (lhs, rhs)"; + + block_open(out); + + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + t_field* tfield = *m_iter; + indent(out) << "case " + << "(." << maybe_escape_identifier(tfield->get_name()) << "(let a), " + << "." << maybe_escape_identifier(tfield->get_name()) << "(let b)): " + << "return a == b" << endl; + } + + // Probably need this to avoid surprises + indent(out) << "case (.ThriftUnknownValue, .ThriftUnknownValue): return true" << endl; + indent(out) << "default: return false" << endl; + + block_close(out); + + block_close(out); + + out << endl; +} + +/** + * Generate the hashable protocol implementation for a union + * + * @param tstruct The structure definition + */ +void t_swift_generator::generate_swift_union_hashable_extension(ofstream& out, t_struct* tstruct) { + + indent(out) << "extension " << tstruct->get_name() << " : Hashable"; + block_open(out); + + out << endl; + + indent(out) << "public var hashValue : Int"; + block_open(out); + + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + indent(out) << "let prime = 31" << endl; + + indent(out) << "switch self"; + block_open(out); + + string exponated_prime = "prime"; + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + t_field* tfield = *m_iter; + indent(out) << "case ." << maybe_escape_identifier(tfield->get_name()) << "(let a): " + << "return " << exponated_prime << " &+ a.hashValue" << endl; + exponated_prime += " &* prime"; + } + + indent(out) << "case .ThriftUnknownValue: " + << "return " << exponated_prime << endl; + + block_close(out); + + block_close(out); + + out << endl; + + block_close(out); + + out << endl; +} + +/** + * Generate the TStruct protocol implementation for a union + * + * @param tstruct The structure definition + */ +void t_swift_generator::generate_swift_union_thrift_extension(ofstream& out, t_struct* tstruct) { + + indent(out) << "extension " << tstruct->get_name() << " : TStruct"; + + block_open(out); + + out << endl; + + generate_swift_union_reader(out, tstruct); + + generate_swift_union_writer(out, tstruct); + + block_close(out); + + out << endl; +} + +/** + * Generates a function to read a union from + * from a protocol. (TStruct compliance) + * + * @param tstruct The structure definition + */ +void t_swift_generator::generate_swift_union_reader(ofstream& out, t_struct* tstruct) { + + indent(out) << "public static func readValueFromProtocol(__proto: TProtocol) throws -> " + << tstruct->get_name(); + + block_open(out); + + out << endl; + + indent(out) << "try __proto.readStructBegin()" << endl << endl; + + indent(out) << "var __value : " << tstruct->get_name() << "!" << endl; + + indent(out) << "let (_, fieldType, fieldID) = try __proto.readFieldBegin()" << endl << endl; + + indent(out) << "switch (fieldID, fieldType)"; + block_open(out); + + // Union should contain exactly one value, if it contains less, + // signal a protol error + indent(out) << "case (_, .STOP):" << endl; + indent_up(); + // TODO: will need to throw a better error here + indent(out) << "throw NSError(" << endl; + indent_up(); + indent(out) << "domain: TProtocolErrorDomain," << endl; + indent(out) << "code: Int(TProtocolError.InvalidData.rawValue)," << endl; + indent(out) << "userInfo: [TProtocolErrorExtendedErrorKey: TProtocolExtendedError.MissingRequiredField.rawValue])" << endl << endl; + indent_down(); + indent_down(); + + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + // Generate deserialization code for known cases + 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; + indent_up(); + indent(out) << "let __v = try __proto.readValue() as " + << type_name((*f_iter)->get_type()) << endl; + indent(out) << "__value = ." << maybe_escape_identifier((*f_iter)->get_name()) << "(__v)" << endl << endl; + indent_down(); + } + + // Unknown value + indent(out) << "case let (_, unknownType):" << endl; + indent_up(); + indent(out) << "try __proto.skipType(unknownType)" << endl; + indent(out) << "__value = .ThriftUnknownValue" << endl; + indent_down(); + + block_close(out); + + indent(out) << "try __proto.readFieldEnd()" << endl; + + // Try to read another field. We expect exactly one field, so the only valid value here is .STOP + indent(out) << "let (_, secondFieldType, secondFieldID) = try __proto.readFieldBegin()" << endl << endl; + indent(out) << "if secondFieldType != .STOP"; + block_open(out); + // TODO: will need to throw a better error here + indent(out) << "throw NSError(" << endl; + indent_up(); + indent(out) << "domain: TProtocolErrorDomain," << endl; + indent(out) << "code: Int(TProtocolError.InvalidData.rawValue)," << endl; + indent(out) << "userInfo: [TProtocolErrorExtendedErrorKey: secondFieldID])" << endl; + indent_down(); + block_close(out); + + indent(out) << "try __proto.readFieldEnd()" << endl; + + out << endl; + + indent(out) << "try __proto.readStructEnd()" << endl; + + indent(out) << "return __value" << endl; + + block_close(out); + + out << endl; +} + + +/** + * Generates a function to write a union to + * a protocol. (TStruct compliance) + * + * @param tstruct The structure definition + */ +void t_swift_generator::generate_swift_union_writer(ofstream& out, t_struct* tstruct) { + + indent(out) << "public static func writeValue(__value: " << tstruct->get_name() << ", toProtocol __proto: TProtocol) throws"; + + block_open(out); + + out << endl; + + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + indent(out) << "try __proto.writeStructBeginWithName(\"" << tstruct->get_name() << "\")" << endl; + + out << endl; + + indent(out) << "switch __value"; + block_open(out); + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + t_field *tfield = *f_iter; + + indent(out) << "case ." << maybe_escape_identifier(tfield->get_name()) << "(let __v):" << endl; + indent_up(); + indent(out) << "try __proto.writeFieldValue(" + << "__v" << ", " + << "name: \"" << tfield->get_name() << "\", " + << "type: " << type_to_enum(tfield->get_type()) << ", " + << "id: " << tfield->get_key() << ")" << endl; + indent_down(); + + out << endl; + } + + indent(out) << "case .ThriftUnknownValue:" << endl; + // TODO: Will need to throw a better error here + indent_up(); + indent(out) << "throw NSError(" << endl; + indent_up(); + indent(out) << "domain: TProtocolErrorDomain, " << endl; + indent(out) << "code: Int(TProtocolError.InvalidData.rawValue)," << endl; + indent(out) << "userInfo: [])" << endl; + indent_down(); + indent_down(); + + block_close(out); + + indent(out) << "try __proto.writeFieldStop()" << endl << endl; + + indent(out) << "try __proto.writeStructEnd()" << endl; + + block_close(out); + + out << endl; +} + /** * Generates a thrift service. In Swift this consists of a * protocol definition and a client (with it's implementation diff --git a/configure.ac b/configure.ac index bc52adf6ef4..1f1c7185f66 100755 --- a/configure.ac +++ b/configure.ac @@ -20,7 +20,7 @@ AC_PREREQ(2.65) AC_CONFIG_MACRO_DIR([./aclocal]) -AC_INIT([thrift], [1.0.0-dev]) +AC_INIT([thrift], [1.0.0-dev.zedge-20160408.2]) AC_CONFIG_AUX_DIR([.])