Skip to content

Commit

Permalink
Handle mapping struct fields with subtypes to Objective-C (#319)
Browse files Browse the repository at this point in the history
  • Loading branch information
julianlocke authored Feb 6, 2024
1 parent e5d9907 commit f4cf70d
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 3 deletions.
32 changes: 31 additions & 1 deletion stone/backends/swift_rsrc/ObjcTypes.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,37 @@ public class DBX{{ namespace_class_name }}{{ data_type_class_name }}: {{ 'NSObje
{% for field in data_type.fields %}
{{ struct_field_doc(field, ' ') }}
@objc
public var {{ fmt_var(field.name) }}: {{ fmt_objc_type(field.data_type) }} { {{ objc_return_field_value(data_type, field) }} }
{% if objc_return_field_value_specified_in_jinja(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 %}
}
{% 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)
}
}
{% endif %}
}
{% else %}
public var {{ fmt_var(field.name) }}: {{ fmt_objc_type(field.data_type) }} { {{ objc_return_field_value_oneliner(data_type, field) }} }
{% endif %}
{% endfor %}
{% if data_type.fields %}

Expand Down
73 changes: 71 additions & 2 deletions stone/backends/swift_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,16 @@ def generate(self, api):
template_globals['data_objc_type_doc'] = self._data_objc_type_doc
template_globals['objc_init_args'] = self._objc_init_args
template_globals['fmt_objc_type'] = fmt_objc_type
template_globals['objc_return_field_value'] = self._objc_return_field_value
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['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 @@ -334,7 +343,67 @@ def _route_schema_attrs(self, route_schema, route):
result = ',\n '.join(attrs)
return result

def _objc_return_field_value(self, parent_type, field):
# 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 f4cf70d

Please sign in to comment.