diff --git a/stone/backends/swift_client.py b/stone/backends/swift_client.py index b9929f7f..52d9a236 100644 --- a/stone/backends/swift_client.py +++ b/stone/backends/swift_client.py @@ -25,6 +25,7 @@ fmt_type, fmt_objc_type, mapped_list_info, + datatype_has_subtypes, ) from stone.ir import ( is_struct_type, @@ -605,6 +606,8 @@ def _objc_result_from_swift(self, result_data_type, swift_var_name='swift'): if is_user_defined_type(list_data_type): objc_type = fmt_objc_type(list_data_type, False) factory_func = '.factory' if is_union_type(list_data_type) else '' + factory_func = '.wrapPreservingSubtypes' if datatype_has_subtypes(data_type) \ + else factory_func value = '{}.map {}{{ {}{}(swift: $0) }}'.format(value, prefix, objc_type, @@ -624,6 +627,8 @@ def _objc_result_from_swift(self, result_data_type, swift_var_name='swift'): else: objc_data_type = fmt_objc_type(data_type) factory_func = '.factory' if is_union_type(data_type) else '' + factory_func = '.wrapPreservingSubtypes' if datatype_has_subtypes(data_type) \ + else factory_func return '{}{}(swift: {})'.format(objc_data_type, factory_func, swift_var_name) diff --git a/stone/backends/swift_helpers.py b/stone/backends/swift_helpers.py index 1c28452a..35527957 100644 --- a/stone/backends/swift_helpers.py +++ b/stone/backends/swift_helpers.py @@ -14,6 +14,7 @@ UInt32, UInt64, Void, + is_struct_type, is_boolean_type, is_list_type, is_map_type, @@ -236,3 +237,54 @@ def mapped_list_info(data_type): suffix = '{} }}'.format(suffix) return (list_depth, prefix, suffix, list_data_type, list_nullable) + +def field_is_user_defined(field): + data_type, nullable = unwrap_nullable(field.data_type) + return is_user_defined_type(data_type) and not nullable + +def field_is_user_defined_optional(field): + data_type, nullable = unwrap_nullable(field.data_type) + return is_user_defined_type(data_type) and nullable + +def field_is_user_defined_map(field): + data_type, _ = unwrap_nullable(field.data_type) + return is_map_type(data_type) and is_user_defined_type(data_type.value_data_type) + +def field_is_user_defined_list(field): + data_type, _ = unwrap_nullable(field.data_type) + if is_list_type(data_type): + list_data_type, _ = unwrap_nullable(data_type.data_type) + return is_user_defined_type(list_data_type) + else: + return False + +# List[typing.Tuple[let_name: str, swift_type: str, objc_type: str]] +def objc_datatype_value_type_tuples(data_type): + ret = [] + + # if list type get the data type of the item + if is_list_type(data_type): + data_type = data_type.data_type + + # if map type get the data type of the value + if is_map_type(data_type): + data_type = data_type.value_data_type + + # if data_type is a struct type and has subtypes, process them into labels and types + if is_struct_type(data_type) and data_type.has_enumerated_subtypes(): + all_subtypes = data_type.get_all_subtypes_with_tags() + + for subtype in all_subtypes: + # subtype[0] is the tag name and subtype[1] is the subtype struct itself + struct = subtype[1] + case_let_name = fmt_var(struct.name) + swift_type = fmt_type(struct) + objc_type = fmt_objc_type(struct) + ret.append((case_let_name, swift_type, objc_type)) + return ret + +def field_datatype_has_subtypes(field) -> bool: + return datatype_has_subtypes(field.data_type) + +def datatype_has_subtypes(data_type) -> bool: + return len(objc_datatype_value_type_tuples(data_type)) > 0 diff --git a/stone/backends/swift_rsrc/ObjcTypes.jinja b/stone/backends/swift_rsrc/ObjcTypes.jinja index a0444807..10bd3491 100644 --- a/stone/backends/swift_rsrc/ObjcTypes.jinja +++ b/stone/backends/swift_rsrc/ObjcTypes.jinja @@ -22,31 +22,17 @@ public class DBX{{ namespace_class_name }}{{ data_type_class_name }}: {{ 'NSObje {% for field in data_type.fields %} {{ struct_field_doc(field, ' ') }} @objc - {% if objc_return_field_value_specified_in_jinja(field) %} + {% if field_datatype_has_subtypes(field) %} public var {{ fmt_var(field.name) }}: {{ fmt_objc_type(field.data_type) }} { {% if (field_is_user_defined(field)) or (field_is_user_defined_optional(field)) %} - switch {{ swift_var_name }}.{{ fmt_var(field.name) }} { - {% for tuple in objc_return_field_value_type_tuples(field) %} - case let {{ tuple[0] }} as {{ tuple[1] }}: - return {{ tuple[2] }}(swift: {{ tuple[0] }}) - {% endfor %} - default: - {% if field_is_user_defined_optional(field) %} - return {{ swift_var_name }}.{{ fmt_var(field.name) }}.flatMap { {{ fmt_objc_type(field.data_type, False) }}(swift: $0) } - {% else %} - return {{ fmt_objc_type(field.data_type) }}(swift: {{ swift_var_name }}.{{ fmt_var(field.name) }}) - {% endif %} - } + {% if field_is_user_defined_optional(field) %} + return {{ swift_var_name }}.{{ fmt_var(field.name) }}.flatMap { {{ fmt_objc_type(field.data_type, False) }}.wrapPreservingSubtypes(swift: $0) } + {% else %} + return {{ fmt_objc_type(field.data_type) }}.wrapPreservingSubtypes(swift: {{ swift_var_name }}.{{ fmt_var(field.name) }}) + {% endif %} {% elif (field_is_user_defined_map(field)) or (field_is_user_defined_list(field)) %} {{ swift_var_name }}.{{ fmt_var(field.name) }}.{{ 'mapValues' if field_is_user_defined_map(field) else 'map' }} { - switch $0 { - {% for tuple in objc_return_field_value_type_tuples(field) %} {#extract this snippet?#} - case let {{ tuple[0] }} as {{ tuple[1] }}: - return {{ tuple[2] }}(swift: {{ tuple[0] }}) - {% endfor %} - default: - return {{ fmt_objc_type(field.data_type.data_type) }}(swift: $0) - } + return {{ fmt_objc_type(field.data_type.data_type) }}.wrapPreservingSubtypes(swift: $0) } {% endif %} } @@ -77,6 +63,19 @@ public class DBX{{ namespace_class_name }}{{ data_type_class_name }}: {{ 'NSObje {% endif %} } + {% if objc_datatype_value_type_tuples(data_type)|length > 0 %} + public static func wrapPreservingSubtypes(swift: {{ swift_type }}) -> DBX{{ namespace_class_name }}{{ data_type_class_name }} { + switch swift { + {% for tuple in objc_datatype_value_type_tuples(data_type) %} + case let {{ tuple[0] }} as {{ tuple[1] }}: + return {{ tuple[2] }}(swift: {{ tuple[0] }}) + {% endfor %} + default: + return DBX{{ namespace_class_name }}{{ data_type_class_name }}(swift: swift) + } + } + {% endif %} + @objc public override var description: String { {{ 'swift' if not data_type.parent_type else 'subSwift' }}.description } } diff --git a/stone/backends/swift_types.py b/stone/backends/swift_types.py index 22e03e5a..275517e3 100644 --- a/stone/backends/swift_types.py +++ b/stone/backends/swift_types.py @@ -25,7 +25,12 @@ fmt_route_name, fmt_objc_type, mapped_list_info, -) + field_is_user_defined, + field_is_user_defined_optional, + field_is_user_defined_map, + field_is_user_defined_list, + objc_datatype_value_type_tuples, + field_datatype_has_subtypes, from stone.ir import ( is_list_type, @@ -177,14 +182,13 @@ def generate(self, api): template_globals['fmt_objc_type'] = fmt_objc_type oneliner_func_key = 'objc_return_field_value_oneliner' template_globals[oneliner_func_key] = self._objc_return_field_value_oneliner - template_globals['field_is_user_defined'] = self._field_is_user_defined - template_globals['field_is_user_defined_optional'] = self._field_is_user_defined_optional - template_globals['field_is_user_defined_list'] = self._field_is_user_defined_list - template_globals['field_is_user_defined_map'] = self._field_is_user_defined_map - in_jinja_key = 'objc_return_field_value_specified_in_jinja' - template_globals[in_jinja_key] = self._objc_return_field_value_specified_in_jinja - field_value_tuples_key = 'objc_return_field_value_type_tuples' - template_globals[field_value_tuples_key] = self._objc_return_field_value_type_tuples + template_globals['field_is_user_defined'] = field_is_user_defined + template_globals['field_is_user_defined_optional'] = field_is_user_defined_optional + template_globals['field_is_user_defined_list'] = field_is_user_defined_list + template_globals['field_is_user_defined_map'] = field_is_user_defined_map + in_jinja_key = 'field_datatype_has_subtypes' + template_globals[in_jinja_key] = field_datatype_has_subtypes + template_globals['objc_datatype_value_type_tuples'] = objc_datatype_value_type_tuples template_globals['objc_init_args_to_swift'] = self._objc_init_args_to_swift template_globals['objc_union_arg'] = self._objc_union_arg template_globals['objc_swift_var_name'] = self._objc_swift_var_name @@ -349,66 +353,6 @@ def _route_schema_attrs(self, route_schema, route): result = ',\n '.join(attrs) return result - # List[typing.Tuple[let_name: str, swift_type: str, objc_type: str]] - def _objc_return_field_value_type_tuples(self, field): - data_type = field.data_type - ret = [] - - # if list type get the data type of the item - if is_list_type(data_type): - data_type = data_type.data_type - - # if map type get the data type of the value - if is_map_type(data_type): - data_type = data_type.value_data_type - - # if data_type is a struct type and has subtypes, process them into labels and types - if is_struct_type(data_type) and data_type.has_enumerated_subtypes(): - all_subtypes = data_type.get_all_subtypes_with_tags() - - for subtype in all_subtypes: - # subtype[0] is the tag name and subtype[1] is the subtype struct itself - struct = subtype[1] - case_let_name = fmt_var(struct.name) - swift_type = fmt_type(struct) - objc_type = fmt_objc_type(struct) - ret.append((case_let_name, swift_type, objc_type)) - - return ret - - def _field_is_user_defined(self, field): - data_type, nullable = unwrap_nullable(field.data_type) - return is_user_defined_type(data_type) and not nullable - - def _field_is_user_defined_optional(self, field): - data_type, nullable = unwrap_nullable(field.data_type) - return is_user_defined_type(data_type) and nullable - - def _field_is_user_defined_map(self, field): - data_type, _ = unwrap_nullable(field.data_type) - return is_map_type(data_type) and is_user_defined_type(data_type.value_data_type) - - def _field_is_user_defined_list(self, field): - data_type, _ = unwrap_nullable(field.data_type) - if is_list_type(data_type): - list_data_type, _ = unwrap_nullable(data_type.data_type) - return is_user_defined_type(list_data_type) - else: - return False - - def _objc_return_field_value_specified_in_jinja(self, field) -> bool: - eligible_kind = self._field_is_user_defined(field) or \ - self._field_is_user_defined_optional(field) or \ - self._field_is_user_defined_map(field) or \ - self._field_is_user_defined_list(field) - - if not eligible_kind: - return False - - requires_iterating_over_subtypes = len(self._objc_return_field_value_type_tuples(field)) > 0 - - return requires_iterating_over_subtypes - def _objc_return_field_value_oneliner(self, parent_type, field): data_type, nullable = unwrap_nullable(field.data_type) swift_var_name = self._objc_swift_var_name(parent_type)