Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add subtype preserving objective c wrapper to two more locations #321

Merged
merged 1 commit into from
Feb 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions stone/backends/swift_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
fmt_type,
fmt_objc_type,
mapped_list_info,
datatype_has_subtypes,
)

_MYPY = False
Expand Down Expand Up @@ -141,7 +142,7 @@
self._generate_client(api)
self._generate_request_boxes(api)
if not self.args.objc:
self._generate_reconnection_helpers(api)

Check warning on line 145 in stone/backends/swift_client.py

View check run for this annotation

Codecov / codecov/patch

stone/backends/swift_client.py#L145

Added line #L145 was not covered by tests

def _generate_client(self, api):
template_globals = {}
Expand All @@ -167,7 +168,7 @@
namespace_fields = []
for namespace in api.namespaces.values():
if self._namespace_contains_valid_routes_for_auth_type(namespace):
namespace_fields.append((fmt_var(namespace.name),

Check warning on line 171 in stone/backends/swift_client.py

View check run for this annotation

Codecov / codecov/patch

stone/backends/swift_client.py#L171

Added line #L171 was not covered by tests
fmt_class(self._class_name(namespace.name))))

return namespace_fields
Expand Down Expand Up @@ -205,7 +206,7 @@
template_globals['objc_init_args_to_swift'] = self._objc_init_args_to_swift
template_globals['objc_result_from_swift'] = self._objc_result_from_swift
template_globals['objc_no_defualts_func_args'] = self._objc_no_defualts_func_args
template_globals['objc_app_auth_route_wrapper_already_defined'] = \

Check warning on line 209 in stone/backends/swift_client.py

View check run for this annotation

Codecov / codecov/patch

stone/backends/swift_client.py#L209

Added line #L209 was not covered by tests
self._objc_app_auth_route_wrapper_already_defined

ns_class = self._class_name(fmt_class(namespace.name))
Expand Down Expand Up @@ -239,17 +240,17 @@
template_globals['request_type_signature'] = self._request_type_signature
template_globals['fmt_func'] = fmt_func
template_globals['fmt_route_objc_class'] = self._fmt_route_objc_class
swift_class_name = '{}RequestBox'.format(self.args.class_name)

Check warning on line 243 in stone/backends/swift_client.py

View check run for this annotation

Codecov / codecov/patch

stone/backends/swift_client.py#L243

Added line #L243 was not covered by tests

if self.args.objc:
template = self._jinja_template("ObjCRequestBox.jinja")
template.globals = template_globals

# don't include the default case in the generated switch statement if it's unreachable
include_default_in_switch = \

Check warning on line 250 in stone/backends/swift_client.py

View check run for this annotation

Codecov / codecov/patch

stone/backends/swift_client.py#L250

Added line #L250 was not covered by tests
len(background_objc_routes) < len(background_compatible_routes)

output = template.render(

Check warning on line 253 in stone/backends/swift_client.py

View check run for this annotation

Codecov / codecov/patch

stone/backends/swift_client.py#L253

Added line #L253 was not covered by tests
background_compatible_routes=background_compatible_routes,
background_objc_routes=background_objc_routes,
class_name=swift_class_name,
Expand All @@ -275,22 +276,22 @@
if len(background_compatible_pairs) == 0:
return

is_app_auth_client = self.args.auth_type == 'app'
class_name_prefix = 'AppAuth' if is_app_auth_client else ''
class_name = '{}ReconnectionHelpers'.format(class_name_prefix)
return_type = '{}RequestBox'.format(self.args.class_name)

Check warning on line 282 in stone/backends/swift_client.py

View check run for this annotation

Codecov / codecov/patch

stone/backends/swift_client.py#L279-L282

Added lines #L279 - L282 were not covered by tests

template = self._jinja_template("SwiftReconnectionHelpers.jinja")
template.globals['fmt_func'] = fmt_func
template.globals['fmt_class'] = fmt_class
template.globals['class_name'] = class_name
template.globals['return_type'] = return_type

Check warning on line 288 in stone/backends/swift_client.py

View check run for this annotation

Codecov / codecov/patch

stone/backends/swift_client.py#L287-L288

Added lines #L287 - L288 were not covered by tests

output_from_parsed_template = template.render(
background_compatible_namespace_route_pairs=background_compatible_pairs
)

self._write_output_in_target_folder(

Check warning on line 294 in stone/backends/swift_client.py

View check run for this annotation

Codecov / codecov/patch

stone/backends/swift_client.py#L294

Added line #L294 was not covered by tests
output_from_parsed_template, '{}.swift'.format(class_name)
)

Expand Down Expand Up @@ -339,23 +340,23 @@
# if building the user or team client, include routes of all auth types except
# app auth exclusive routes

is_app_auth_only_route = route_auth_type == 'app'
route_auth_types_include_app = 'app' in route_auth_type

Check warning on line 344 in stone/backends/swift_client.py

View check run for this annotation

Codecov / codecov/patch

stone/backends/swift_client.py#L343-L344

Added lines #L343 - L344 were not covered by tests

if client_auth_type == 'app':
return is_app_auth_only_route or route_auth_types_include_app

Check warning on line 347 in stone/backends/swift_client.py

View check run for this annotation

Codecov / codecov/patch

stone/backends/swift_client.py#L347

Added line #L347 was not covered by tests
else:
return not is_app_auth_only_route

Check warning on line 349 in stone/backends/swift_client.py

View check run for this annotation

Codecov / codecov/patch

stone/backends/swift_client.py#L349

Added line #L349 was not covered by tests

# The objc compatibility wrapper generates a class to wrap each route providing properly
# typed completion handlers without generics. User and App clients are generated in separate
# passes, and if the wrapper is already defined for the user client, we must skip generating
# a second definition of it for the app client.
def _objc_app_auth_route_wrapper_already_defined(self, route):
client_auth_type = self.args.auth_type
is_app_auth_client = client_auth_type == 'app'

Check warning on line 357 in stone/backends/swift_client.py

View check run for this annotation

Codecov / codecov/patch

stone/backends/swift_client.py#L355-L357

Added lines #L355 - L357 were not covered by tests

return is_app_auth_client and route.attrs.get('auth') != 'app'

Check warning on line 359 in stone/backends/swift_client.py

View check run for this annotation

Codecov / codecov/patch

stone/backends/swift_client.py#L359

Added line #L359 was not covered by tests

def _namespace_contains_valid_routes_for_auth_type(self, namespace):
valid_count = 0
Expand Down Expand Up @@ -564,7 +565,7 @@
objc_class_to_route = {}
for namespace in namespaces:
for route in namespace.routes:
bg_route_style = self._background_session_route_style(route)

Check warning on line 568 in stone/backends/swift_client.py

View check run for this annotation

Codecov / codecov/patch

stone/backends/swift_client.py#L568

Added line #L568 was not covered by tests
if bg_route_style is not None and self._valid_route_for_auth_type(route):
args_data = self._route_client_args(route)[0]
objc_class = self._fmt_route_objc_class(namespace, route, args_data)
Expand Down Expand Up @@ -602,6 +603,8 @@
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) \

Check warning on line 606 in stone/backends/swift_client.py

View check run for this annotation

Codecov / codecov/patch

stone/backends/swift_client.py#L606

Added line #L606 was not covered by tests
else factory_func
value = '{}.map {}{{ {}{}(swift: $0) }}'.format(value,
prefix,
objc_type,
Expand All @@ -621,6 +624,8 @@
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) \

Check warning on line 627 in stone/backends/swift_client.py

View check run for this annotation

Codecov / codecov/patch

stone/backends/swift_client.py#L627

Added line #L627 was not covered by tests
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 @@ -16,6 +16,7 @@
UInt32,
UInt64,
Void,
is_struct_type,
is_boolean_type,
is_list_type,
is_map_type,
Expand Down Expand Up @@ -238,3 +239,54 @@
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

Check warning on line 245 in stone/backends/swift_helpers.py

View check run for this annotation

Codecov / codecov/patch

stone/backends/swift_helpers.py#L243-L245

Added lines #L243 - L245 were not covered by tests

def field_is_user_defined_optional(field):
data_type, nullable = unwrap_nullable(field.data_type)
return is_user_defined_type(data_type) and nullable

Check warning on line 249 in stone/backends/swift_helpers.py

View check run for this annotation

Codecov / codecov/patch

stone/backends/swift_helpers.py#L247-L249

Added lines #L247 - L249 were not covered by tests

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)

Check warning on line 253 in stone/backends/swift_helpers.py

View check run for this annotation

Codecov / codecov/patch

stone/backends/swift_helpers.py#L251-L253

Added lines #L251 - L253 were not covered by tests

def field_is_user_defined_list(field):
data_type, _ = unwrap_nullable(field.data_type)

Check warning on line 256 in stone/backends/swift_helpers.py

View check run for this annotation

Codecov / codecov/patch

stone/backends/swift_helpers.py#L255-L256

Added lines #L255 - L256 were not covered by tests
if is_list_type(data_type):
list_data_type, _ = unwrap_nullable(data_type.data_type)
return is_user_defined_type(list_data_type)

Check warning on line 259 in stone/backends/swift_helpers.py

View check run for this annotation

Codecov / codecov/patch

stone/backends/swift_helpers.py#L258-L259

Added lines #L258 - L259 were not covered by tests
else:
return False

Check warning on line 261 in stone/backends/swift_helpers.py

View check run for this annotation

Codecov / codecov/patch

stone/backends/swift_helpers.py#L261

Added line #L261 was not covered by tests

# List[typing.Tuple[let_name: str, swift_type: str, objc_type: str]]
def objc_datatype_value_type_tuples(data_type):
ret = []

Check warning on line 265 in stone/backends/swift_helpers.py

View check run for this annotation

Codecov / codecov/patch

stone/backends/swift_helpers.py#L264-L265

Added lines #L264 - L265 were not covered by tests

# if list type get the data type of the item
if is_list_type(data_type):
data_type = data_type.data_type

Check warning on line 269 in stone/backends/swift_helpers.py

View check run for this annotation

Codecov / codecov/patch

stone/backends/swift_helpers.py#L269

Added line #L269 was not covered by tests

# if map type get the data type of the value
if is_map_type(data_type):
data_type = data_type.value_data_type

Check warning on line 273 in stone/backends/swift_helpers.py

View check run for this annotation

Codecov / codecov/patch

stone/backends/swift_helpers.py#L273

Added line #L273 was not covered by tests

# 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()

Check warning on line 277 in stone/backends/swift_helpers.py

View check run for this annotation

Codecov / codecov/patch

stone/backends/swift_helpers.py#L277

Added line #L277 was not covered by tests

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

Check warning on line 286 in stone/backends/swift_helpers.py

View check run for this annotation

Codecov / codecov/patch

stone/backends/swift_helpers.py#L281-L286

Added lines #L281 - L286 were not covered by tests

def field_datatype_has_subtypes(field) -> bool:
return datatype_has_subtypes(field.data_type)

Check warning on line 289 in stone/backends/swift_helpers.py

View check run for this annotation

Codecov / codecov/patch

stone/backends/swift_helpers.py#L288-L289

Added lines #L288 - L289 were not covered by tests

def datatype_has_subtypes(data_type) -> bool:
return len(objc_datatype_value_type_tuples(data_type)) > 0

Check warning on line 292 in stone/backends/swift_helpers.py

View check run for this annotation

Codecov / codecov/patch

stone/backends/swift_helpers.py#L291-L292

Added lines #L291 - L292 were not covered by tests
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: 14 additions & 68 deletions stone/backends/swift_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,14 @@
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.backends.swift import (
fmt_serial_obj,
SwiftBaseBackend,
Expand Down Expand Up @@ -132,14 +139,14 @@
def generate(self, api):
rsrc_folder = os.path.join(os.path.dirname(__file__), 'swift_rsrc')
if not self.args.objc:
self.logger.info('Copying StoneValidators.swift to output folder')
shutil.copy(os.path.join(rsrc_folder, 'StoneValidators.swift'),

Check warning on line 143 in stone/backends/swift_types.py

View check run for this annotation

Codecov / codecov/patch

stone/backends/swift_types.py#L142-L143

Added lines #L142 - L143 were not covered by tests
self.target_folder_path)
self.logger.info('Copying StoneSerializers.swift to output folder')
shutil.copy(os.path.join(rsrc_folder, 'StoneSerializers.swift'),

Check warning on line 146 in stone/backends/swift_types.py

View check run for this annotation

Codecov / codecov/patch

stone/backends/swift_types.py#L145-L146

Added lines #L145 - L146 were not covered by tests
self.target_folder_path)
self.logger.info('Copying StoneBase.swift to output folder')
shutil.copy(os.path.join(rsrc_folder, 'StoneBase.swift'),

Check warning on line 149 in stone/backends/swift_types.py

View check run for this annotation

Codecov / codecov/patch

stone/backends/swift_types.py#L148-L149

Added lines #L148 - L149 were not covered by tests
self.target_folder_path)

template_loader = jinja2.FileSystemLoader(searchpath=rsrc_folder)
Expand Down Expand Up @@ -172,16 +179,15 @@
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
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

Check warning on line 190 in stone/backends/swift_types.py

View check run for this annotation

Codecov / codecov/patch

stone/backends/swift_types.py#L182-L190

Added lines #L182 - L190 were not covered by tests
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 @@ -210,15 +216,15 @@
self._write_output_in_target_folder(swift_output,
'{}.swift'.format(ns_class))
if self.args.documentation:
self._generate_jazzy_docs(api)

Check warning on line 219 in stone/backends/swift_types.py

View check run for this annotation

Codecov / codecov/patch

stone/backends/swift_types.py#L219

Added line #L219 was not covered by tests

def _generate_jazzy_docs(self, api):
jazzy_cfg_path = os.path.join('../Format', 'jazzy.json')
with open(jazzy_cfg_path, encoding='utf-8') as jazzy_file:
jazzy_cfg = json.load(jazzy_file)

Check warning on line 224 in stone/backends/swift_types.py

View check run for this annotation

Codecov / codecov/patch

stone/backends/swift_types.py#L221-L224

Added lines #L221 - L224 were not covered by tests

for namespace in api.namespaces.values():
ns_class = fmt_class(namespace.name)

Check warning on line 227 in stone/backends/swift_types.py

View check run for this annotation

Codecov / codecov/patch

stone/backends/swift_types.py#L227

Added line #L227 was not covered by tests
jazzy_cfg['custom_categories'][1]['children'].append(ns_class)

if namespace.routes:
Expand Down Expand Up @@ -346,67 +352,7 @@
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):

Check warning on line 355 in stone/backends/swift_types.py

View check run for this annotation

Codecov / codecov/patch

stone/backends/swift_types.py#L355

Added line #L355 was not covered by tests
data_type, nullable = unwrap_nullable(field.data_type)
swift_var_name = self._objc_swift_var_name(parent_type)

Expand Down
Loading