def _get_namespace_route_imports(self, namespace, include_route_args=True, include_route_deep_args=False): result = [] def _unpack_and_store_data_type(data_type): data_type, _ = unwrap_nullable(data_type) if is_list_type(data_type): while is_list_type(data_type): data_type = data_type.data_type if not is_void_type(data_type) and is_user_defined_type(data_type): result.append(data_type) for route in namespace.routes: if include_route_args: data_type, _ = unwrap_nullable(route.arg_data_type) _unpack_and_store_data_type(data_type) elif include_route_deep_args: data_type, _ = unwrap_nullable(route.arg_data_type) if is_union_type(data_type) or is_list_type(data_type): _unpack_and_store_data_type(data_type) elif not is_void_type(data_type): for field in data_type.all_fields: data_type, _ = unwrap_nullable(field.data_type) if (is_struct_type(data_type) or is_union_type(data_type) or is_list_type(data_type)): _unpack_and_store_data_type(data_type) _unpack_and_store_data_type(route.result_data_type) _unpack_and_store_data_type(route.error_data_type) return result
def _get_imports_m(self, data_types, default_imports): """Emits all necessary implementation file imports for the given Stone data type.""" if not isinstance(data_types, list): data_types = [data_types] import_classes = default_imports for data_type in data_types: import_classes.append(fmt_class_prefix(data_type)) if data_type.parent_type: import_classes.append(fmt_class_prefix(data_type.parent_type)) if is_struct_type(data_type) and data_type.has_enumerated_subtypes(): for _, subtype in data_type.get_all_subtypes_with_tags(): import_classes.append(fmt_class_prefix(subtype)) for field in data_type.all_fields: data_type, _ = unwrap_nullable(field.data_type) # unpack list while is_list_type(data_type): data_type = data_type.data_type if is_user_defined_type(data_type): import_classes.append(fmt_class_prefix(data_type)) if import_classes: import_classes = list(set(import_classes)) import_classes.sort() return import_classes
def _get_route_args(self, namespace, route, tag=False): # pylint: disable=unused-argument """Returns a list of name / value string pairs representing the arguments for a particular route.""" data_type, _ = unwrap_nullable(route.arg_data_type) if is_struct_type(data_type): arg_list = [] for field in data_type.all_fields: arg_list.append((fmt_var(field.name), fmt_type( field.data_type, tag=tag, has_default=field.has_default))) doc_list = [(fmt_var(f.name), self.process_doc(f.doc, self._docf)) for f in data_type.fields if f.doc] elif is_union_type(data_type): arg_list = [(fmt_var(data_type.name), fmt_type( route.arg_data_type, tag=tag))] doc_list = [(fmt_var(data_type.name), self.process_doc(data_type.doc, self._docf) if data_type.doc else 'The {} union'.format( fmt_class(data_type .name)))] else: arg_list = [] doc_list = [] return arg_list, doc_list
def _get_default_route_args( self, namespace, # pylint: disable=unused-argument route, tag=False): """Returns a list of name / value string pairs representing the default arguments for a particular route.""" data_type, _ = unwrap_nullable(route.arg_data_type) if is_struct_type(data_type): arg_list = [] for field in data_type.all_fields: if not field.has_default and not is_nullable_type( field.data_type): arg_list.append((fmt_var(field.name), fmt_type( field.data_type, tag=tag))) doc_list = ([(fmt_var(f.name), self.process_doc(f.doc, self._docf)) for f in data_type.fields if f.doc and not f.has_default and not is_nullable_type(f.data_type)]) else: arg_list = [] doc_list = [] return arg_list, doc_list
def _get_imports_h(self, data_types): """Emits all necessary header file imports for the given Stone data type.""" if not isinstance(data_types, list): data_types = [data_types] import_classes = [] for data_type in data_types: if is_user_defined_type(data_type): import_classes.append(fmt_class_prefix(data_type)) for field in data_type.all_fields: data_type, _ = unwrap_nullable(field.data_type) # unpack list while is_list_type(data_type): data_type = data_type.data_type if is_user_defined_type(data_type): import_classes.append(fmt_class_prefix(data_type)) import_classes = list(set(import_classes)) import_classes.sort() return import_classes
def _unpack_and_store_data_type(data_type): data_type, _ = unwrap_nullable(data_type) if is_list_type(data_type): while is_list_type(data_type): data_type = data_type.data_type if not is_void_type(data_type) and is_user_defined_type(data_type): result.append(data_type)
def fmt_class_type(data_type, suppress_ptr=False): data_type, _ = unwrap_nullable(data_type) if is_user_defined_type(data_type): result = '{}'.format(fmt_class_prefix(data_type)) else: result = _primitive_table.get( data_type.__class__, fmt_class(data_type.name)) if suppress_ptr: result = result.replace(' *', '') result = result.replace('*', '') if is_list_type(data_type): data_type, _ = unwrap_nullable(data_type.data_type) result = result + '<{}>'.format(fmt_type(data_type)) return result
def fmt_class_type(data_type, suppress_ptr=False): data_type, _ = unwrap_nullable(data_type) if is_user_defined_type(data_type): result = '{}'.format(fmt_class_prefix(data_type)) else: result = _primitive_table.get(data_type.__class__, fmt_class(data_type.name)) if suppress_ptr: result = result.replace(' *', '') result = result.replace('*', '') if is_list_type(data_type): data_type, _ = unwrap_nullable(data_type.data_type) result = result + '<{}>'.format(fmt_type(data_type)) return result
def generate_validator_constructor(ns, data_type): """ Given a Stone data type, returns a string that can be used to construct the appropriate validation object in Python. """ dt, nullable_dt = unwrap_nullable(data_type) if is_list_type(dt): v = generate_func_call( 'bv.List', args=[ generate_validator_constructor(ns, dt.data_type)], kwargs=[ ('min_items', dt.min_items), ('max_items', dt.max_items)], ) elif is_numeric_type(dt): v = generate_func_call( 'bv.{}'.format(dt.name), kwargs=[ ('min_value', dt.min_value), ('max_value', dt.max_value)], ) elif is_string_type(dt): pattern = None if dt.pattern is not None: pattern = repr(dt.pattern) v = generate_func_call( 'bv.String', kwargs=[ ('min_length', dt.min_length), ('max_length', dt.max_length), ('pattern', pattern)], ) elif is_timestamp_type(dt): v = generate_func_call( 'bv.Timestamp', args=[repr(dt.format)], ) elif is_user_defined_type(dt): v = fmt_class(dt.name) + '_validator' if ns.name != dt.namespace.name: v = '{}.{}'.format(dt.namespace.name, v) elif is_alias(dt): # Assume that the alias has already been declared elsewhere. name = fmt_class(dt.name) + '_validator' if ns.name != dt.namespace.name: name = '{}.{}'.format(dt.namespace.name, name) v = name elif is_boolean_type(dt) or is_bytes_type(dt) or is_void_type(dt): v = generate_func_call('bv.{}'.format(dt.name)) else: raise AssertionError('Unsupported data type: %r' % dt) if nullable_dt: return generate_func_call('bv.Nullable', args=[v]) else: return v
def fmt_serial_obj(data_type): data_type, _ = unwrap_nullable(data_type) if is_user_defined_type(data_type): result = fmt_serial_class(fmt_class_prefix(data_type)) else: result = _serial_table.get( data_type.__class__, fmt_class(data_type.name)) return result
def fmt_serial_obj(data_type): data_type, nullable = unwrap_nullable(data_type) if is_user_defined_type(data_type): result = fmt_serial_class(fmt_class_prefix(data_type)) else: result = _serial_table.get(data_type.__class__, fmt_class(data_type.name)) return result
def fmt_type(data_type, tag=False, has_default=False, no_ptr=False, is_prop=False): data_type, nullable = unwrap_nullable(data_type) if is_user_defined_type(data_type): base = '{}' if no_ptr else '{} *' result = base.format(fmt_class_prefix(data_type)) else: result = _primitive_table.get(data_type.__class__, fmt_class(data_type.name)) if is_list_type(data_type): data_type, _ = unwrap_nullable(data_type.data_type) base = '<{}>' if no_ptr else '<{}> *' result = result + base.format(fmt_type(data_type)) if tag: if (nullable or has_default) and not is_prop: result = 'nullable ' + result return result
def fmt_route_type(data_type, tag=False, has_default=False): data_type, nullable = unwrap_nullable(data_type) if is_user_defined_type(data_type): result = '{} *'.format(fmt_class_prefix(data_type)) else: result = _primitive_table_user_interface.get(data_type.__class__, fmt_class(data_type.name)) if is_list_type(data_type): data_type, _ = unwrap_nullable(data_type.data_type) result = result + '<{}> *'.format(fmt_type(data_type)) if is_user_defined_type(data_type) and tag: if nullable or has_default: result += ' _Nullable' elif not is_void_type(data_type): result += ' _Nonnull' return result
def fmt_route_type(data_type, tag=False, has_default=False): data_type, nullable = unwrap_nullable(data_type) if is_user_defined_type(data_type): result = '{} *'.format(fmt_class_prefix(data_type)) else: result = _primitive_table_user_interface.get( data_type.__class__, fmt_class(data_type.name)) if is_list_type(data_type): data_type, _ = unwrap_nullable(data_type.data_type) result = result + '<{}> *'.format(fmt_type(data_type)) if is_user_defined_type(data_type) and tag: if nullable or has_default: result += ' _Nullable' elif not is_void_type(data_type): result += ' _Nonnull' return result
def fmt_property(field): attrs = ['nonatomic', 'readonly'] data_type, nullable = unwrap_nullable(field.data_type) if is_string_type(data_type): attrs.append('copy') if nullable: attrs.append('nullable') base_string = '@property ({}) {}{};' return base_string.format(', '.join(attrs), fmt_type(field.data_type, tag=True, is_prop=True), fmt_var(field.name))
def fmt_property(field): attrs = ['nonatomic', 'readonly'] data_type, nullable = unwrap_nullable(field.data_type) if is_string_type(data_type): attrs.append('copy') if nullable: attrs.append('nullable') base_string = '@property ({}) {}{};' return base_string.format( ', '.join(attrs), fmt_type(field.data_type, tag=True, is_prop=True), fmt_var(field.name))
def fmt_type(data_type, tag=False, has_default=False, no_ptr=False): data_type, nullable = unwrap_nullable(data_type) if is_user_defined_type(data_type): base = '{}' if no_ptr else '{} *' result = base.format(fmt_class_prefix(data_type)) else: result = _primitive_table.get(data_type.__class__, fmt_class(data_type.name)) if is_list_type(data_type): data_type, _ = unwrap_nullable(data_type.data_type) base = '<{}>' if no_ptr else '<{}> *' result = result + base.format(fmt_type(data_type)) if tag: if nullable or has_default: result += ' _Nullable' elif not is_void_type(data_type): result += ' _Nonnull' return result
def fmt_type(data_type, namespace=None, use_interface=False): data_type, nullable = unwrap_nullable(data_type) if is_list_type(data_type): return "[]%s" % fmt_type(data_type.data_type, namespace, use_interface) type_name = data_type.name if use_interface and _needs_base_type(data_type): type_name = "Is" + type_name if is_composite_type(data_type) and namespace is not None and namespace.name != data_type.namespace.name: type_name = data_type.namespace.name + "." + type_name if use_interface and _needs_base_type(data_type): return _type_table.get(data_type.__class__, type_name) else: return _type_table.get(data_type.__class__, "*" + type_name)
def fmt_type(data_type): data_type, nullable = unwrap_nullable(data_type) if is_user_defined_type(data_type): result = '{}.{}'.format(fmt_class(data_type.namespace.name), fmt_class(data_type.name)) else: result = _type_table.get(data_type.__class__, fmt_class(data_type.name)) if is_list_type(data_type): result = result + '<{}>'.format(fmt_type(data_type.data_type)) return result if not nullable else result + '?'
def fmt_type(data_type, namespace=None, use_interface=False): data_type, nullable = unwrap_nullable(data_type) if is_list_type(data_type): return '[]%s' % fmt_type(data_type.data_type, namespace, use_interface) type_name = data_type.name if use_interface and _needs_base_type(data_type): type_name = 'Is' + type_name if is_composite_type(data_type) and namespace is not None and \ namespace.name != data_type.namespace.name: type_name = data_type.namespace.name + '.' + type_name if use_interface and _needs_base_type(data_type): return _type_table.get(data_type.__class__, type_name) else: return _type_table.get(data_type.__class__, '*' + type_name)
def _struct_init_args(self, data_type, namespace=None): args = [] for field in data_type.all_fields: name = fmt_var(field.name) value = fmt_type(field.data_type) data_type, nullable = unwrap_nullable(field.data_type) if field.has_default: if is_union_type(data_type): default = '.{}'.format(fmt_class(field.default.tag_name)) else: default = fmt_obj(field.default) value += ' = {}'.format(default) elif nullable: value += ' = nil' arg = (name, value) args.append(arg) return args
def fmt_serial_obj(data_type): data_type, nullable = unwrap_nullable(data_type) if is_user_defined_type(data_type): result = '{}.{}Serializer()' result = result.format(fmt_class(data_type.namespace.name), fmt_class(data_type.name)) else: result = _serial_type_table.get(data_type.__class__, fmt_class(data_type.name)) if is_list_type(data_type): result = result + '({})'.format(fmt_serial_obj(data_type.data_type)) elif is_timestamp_type(data_type): result = result + '("{}")'.format(data_type.format) else: result = 'Serialization._{}'.format(result) return result if not nullable else 'NullableSerializer({})'.format(result)
def _struct_init_args(self, data_type, namespace=None): # pylint: disable=unused-argument args = [] for field in data_type.all_fields: name = fmt_var(field.name) value = fmt_type(field.data_type) data_type, nullable = unwrap_nullable(field.data_type) if field.has_default: if is_union_type(data_type): default = '.{}'.format(fmt_var(field.default.tag_name)) else: default = fmt_obj(field.default) value += ' = {}'.format(default) elif nullable: value += ' = nil' arg = (name, value) args.append(arg) return args
def _get_default_route_args(self, namespace, route, tag=False): """Returns a list of name / value string pairs representing the default arguments for a particular route.""" data_type, _ = unwrap_nullable(route.arg_data_type) if is_struct_type(data_type): arg_list = [] for field in data_type.all_fields: if not field.has_default and not is_nullable_type( field.data_type): arg_list.append( (fmt_var(field.name), fmt_type(field.data_type, tag=tag))) doc_list = [(fmt_var(f.name), self.process_doc(f.doc, self._docf)) for f in data_type.fields if f.doc and not f.has_default and not is_nullable_type(f.data_type)] else: arg_list = [] doc_list = [] return arg_list, doc_list
def format_type(typ): """Format a Stone data type as a TypeScript type""" # TODO - Where the type is an alias, emit the alias name and then emit # `type $alias = $wrapped;` """Return a TypeScript representation of a stone DataType""" if data_type.is_nullable_type(typ): wrapped_type, _ = data_type.unwrap_nullable(typ) # If the type is nullable, assume that it has been marked as an # optional member of the containing struct, ie. "field?: type", so we # don't need to represent the fact that it might be null in the # formatted type. ie. There is no need to generate "field?: type | # null" return '{}'.format(format_type(wrapped_type)) if data_type.is_primitive_type(typ): return _primitive_type_map.get(typ.name) elif data_type.is_list_type(typ): return '{}[]'.format(format_type(typ.data_type)) else: return typ.name
def _determine_validator_type(self, data_type, value): data_type, nullable = unwrap_nullable(data_type) if is_list_type(data_type): item_validator = self._determine_validator_type(data_type.data_type, value) if item_validator: v = "arrayValidator({})".format( self._func_args([ ("minItems", data_type.min_items), ("maxItems", data_type.max_items), ("itemValidator", item_validator), ]) ) else: return None elif is_numeric_type(data_type): v = "comparableValidator({})".format( self._func_args([ ("minValue", data_type.min_value), ("maxValue", data_type.max_value), ]) ) elif is_string_type(data_type): pat = data_type.pattern if data_type.pattern else None pat = pat.encode('unicode_escape').replace("\"", "\\\"") if pat else pat v = "stringValidator({})".format( self._func_args([ ("minLength", data_type.min_length), ("maxLength", data_type.max_length), ("pattern", '"{}"'.format(pat) if pat else None), ]) ) else: return None if nullable: v = "nullableValidator({})".format(v) return v
def _generate_struct_type(self, struct_type, indent_spaces, extra_parameters): """ Generates a TypeScript interface for a stone struct. """ namespace = struct_type.namespace if struct_type.doc: self._emit_tsdoc_header(struct_type.doc) parent_type = struct_type.parent_type extends_line = ' extends %s' % fmt_type_name(parent_type, namespace) if parent_type else '' self.emit('interface %s%s {' % (fmt_type_name(struct_type, namespace), extends_line)) with self.indent(dent=indent_spaces): for param_name, param_type, param_docstring in extra_parameters: if param_docstring: self._emit_tsdoc_header(param_docstring) self.emit('%s: %s;' % (param_name, param_type)) for field in struct_type.fields: doc = field.doc field_type, nullable = unwrap_nullable(field.data_type) field_ts_type = fmt_type(field_type, namespace) optional = nullable or field.has_default if field.has_default: # doc may be None. If it is not empty, add newlines # before appending to it. doc = doc + '\n\n' if doc else '' doc = "Defaults to %s." % field.default if doc: self._emit_tsdoc_header(doc) # Translate nullable types into optional properties. field_name = '%s?' % field.name if optional else field.name self.emit('%s: %s;' % (field_name, field_ts_type)) self.emit('}') self.emit() # Some structs can explicitly list their subtypes. These structs have a .tag field that # indicate which subtype they are, which is only present when a type reference is # ambiguous. # Emit a special interface that contains this extra field, and refer to it whenever we # encounter a reference to a type with enumerated subtypes. if struct_type.is_member_of_enumerated_subtypes_tree(): if struct_type.has_enumerated_subtypes(): # This struct is the parent to multiple subtypes. Determine all of the possible # values of the .tag property. tag_values = [] for tags, _ in struct_type.get_all_subtypes_with_tags(): for tag in tags: tag_values.append('"%s"' % tag) tag_union = fmt_union(tag_values) self._emit_tsdoc_header('Reference to the %s polymorphic type. Contains a .tag ' 'property to let you discriminate between possible ' 'subtypes.' % fmt_type_name(struct_type, namespace)) self.emit('interface %s extends %s {' % (fmt_polymorphic_type_reference(struct_type, namespace), fmt_type_name(struct_type, namespace))) with self.indent(dent=indent_spaces): self._emit_tsdoc_header('Tag identifying the subtype variant.') self.emit('\'.tag\': %s;' % tag_union) self.emit('}') self.emit() else: # This struct is a particular subtype. Find the applicable .tag value from the # parent type, which may be an arbitrary number of steps up the inheritance # hierarchy. parent = struct_type.parent_type while not parent.has_enumerated_subtypes(): parent = parent.parent_type # parent now contains the closest parent type in the inheritance hierarchy that has # enumerated subtypes. Determine which subtype this is. for subtype in parent.get_enumerated_subtypes(): if subtype.data_type == struct_type: self._emit_tsdoc_header('Reference to the %s type, identified by the ' 'value of the .tag property.' % fmt_type_name(struct_type, namespace)) self.emit('interface %s extends %s {' % (fmt_polymorphic_type_reference(struct_type, namespace), fmt_type_name(struct_type, namespace))) with self.indent(dent=indent_spaces): self._emit_tsdoc_header('Tag identifying this subtype variant. This ' 'field is only present when needed to ' 'discriminate between multiple possible ' 'subtypes.') self.emit_wrapped_text('\'.tag\': \'%s\';' % subtype.name) self.emit('}') self.emit() break
def is_primitive_type(data_type): data_type, _ = unwrap_nullable(data_type) return data_type.__class__ in _wrapper_primitives
def _get_example_data(self, example_value, field): data_type, nullable = unwrap_nullable(field.data_type) field_name = fmt_var(field.name) result_args = [] if is_user_defined_type(data_type): obj_args = [] if is_union_type(data_type): for field in data_type.all_fields: if field.name == example_value['.tag']: if not is_void_type(field.data_type): if field.name in example_value: self._get_example_data( example_value[field.name], field) else: self._get_example_data(example_value, field) obj_args.append( (fmt_var(field.name), fmt_var(field.name))) field_value = fmt_func_call( caller=fmt_alloc_call(fmt_class_prefix(data_type)), callee='initWith{}'.format( fmt_camel_upper(example_value['.tag'])), args=fmt_func_args(obj_args)) self.emit('{} *{} = {};'.format(fmt_class_prefix(data_type), field_name, field_value)) else: if data_type.has_enumerated_subtypes(): for tags, subtype in data_type.get_all_subtypes_with_tags( ): assert len(tags) == 1, tags tag = tags[0] if tag == example_value['.tag']: self._get_example_data(example_value, subtype) else: for field in data_type.all_fields: if field.name in example_value: obj_args.append((fmt_var(field.name), self._get_example_data( example_value[field.name], field.data_type))) else: if not is_void_type(field.data_type): obj_args.append( (fmt_var(field.name), self._fmt_default(field.data_type))) field_value = fmt_func_call( fmt_alloc_call(fmt_class_prefix(data_type)), 'initWith{}'.format( fmt_camel_upper(data_type.all_fields[0].name)), fmt_func_args(obj_args)) self.emit('{} *{} = {};'.format( fmt_class_prefix(data_type), field_name, field_value)) result_args.append((field_name, field_name)) elif is_list_type(data_type): if example_value: field_value = '@[{}]'.format( self._get_example_data(example_value[0], field)) else: field_value = 'nil' self.emit('NSArray *{} = {};'.format(field_name, field_value)) result_args.append((field_name, field_name)) elif is_numeric_type(data_type): if is_float_type(data_type): field_value = '[NSNumber numberWithDouble:{}]'.format( example_value) elif isinstance(data_type, (UInt64, Int64)): field_value = '[NSNumber numberWithLong:{}]'.format( example_value) else: field_value = '[NSNumber numberWithInt:{}]'.format( example_value) result_args.append((field_name, field_value)) elif is_timestamp_type(data_type): field_value = '[DbxNSDateSerializer deserialize:@"{}" dateFormat:@"{}"]'.format( example_value, data_type.format) self.emit('NSDate *{} = {};'.format(field_name, field_value)) result_args.append((field_name, field_name)) elif is_string_type(data_type): field_value = '@"{}"'.format(example_value) result_args.append((field_name, field_value)) elif is_boolean_type(data_type): field_value = '@YES' if bool(example_value) else '@NO' result_args.append((field_name, field_value)) return result_args