Skip to content

Commit

Permalink
Add subtype preserving objective c wrapper to two more locations (#321)
Browse files Browse the repository at this point in the history
  • Loading branch information
julianlocke committed Aug 27, 2024
1 parent 231e29f commit 7c6b185
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 90 deletions.
5 changes: 5 additions & 0 deletions stone/backends/swift_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
fmt_type,
fmt_objc_type,
mapped_list_info,
datatype_has_subtypes,
)
from stone.ir import (
is_struct_type,
Expand Down Expand Up @@ -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,
Expand All @@ -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)
52 changes: 52 additions & 0 deletions stone/backends/swift_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
UInt32,
UInt64,
Void,
is_struct_type,
is_boolean_type,
is_list_type,
is_map_type,
Expand Down Expand Up @@ -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
41 changes: 20 additions & 21 deletions stone/backends/swift_rsrc/ObjcTypes.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -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 %}
}
Expand Down Expand Up @@ -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 }
}
Expand Down
82 changes: 13 additions & 69 deletions stone/backends/swift_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit 7c6b185

Please sign in to comment.