Exemple #1
0
 def _generate_union(self, union_type):
     """
     Emits a JSDoc @typedef for a union type.
     """
     union_name = fmt_type_name(union_type)
     self._emit_jsdoc_header(union_type.doc)
     self.emit(' * @typedef {Object} %s' % union_name)
     variant_types = []
     for variant in union_type.all_fields:
         variant_types.append("'%s'" % variant.name)
         variant_data_type, _, _ = unwrap(variant.data_type)
         # Don't emit fields for void types.
         if not is_void_type(variant_data_type):
             variant_doc = ' - Available if .tag is %s.' % variant.name
             if variant.doc:
                 variant_doc += ' ' + variant.doc
             self.emit_wrapped_text(
                 '@property {%s} [%s]%s' % (
                     fmt_type(variant_data_type),
                     variant.name,
                     variant_doc,
                 ),
                 prefix=' * ',
             )
     jsdoc_tag_union = fmt_jsdoc_union(variant_types)
     self.emit(' * @property {%s} .tag - Tag identifying the union variant.' % jsdoc_tag_union)
     self.emit(' */')
Exemple #2
0
 def _generate_union(self, union_type):
     """
     Emits a JSDoc @typedef for a union type.
     """
     union_name = fmt_type_name(union_type)
     self._emit_jsdoc_header(union_type.doc)
     self.emit(' * @typedef {Object} %s' % union_name)
     variant_types = []
     for variant in union_type.all_fields:
         variant_types.append("'%s'" % variant.name)
         variant_data_type, _, _ = unwrap(variant.data_type)
         # Don't emit fields for void types.
         if not is_void_type(variant_data_type):
             variant_doc = ' - Available if .tag is %s.' % variant.name
             if variant.doc:
                 variant_doc += ' ' + variant.doc
             self.emit_wrapped_text(
                 '@property {%s} [%s]%s' % (
                     fmt_type(variant_data_type),
                     variant.name,
                     variant_doc,
                 ),
                 prefix=' * ',
             )
     jsdoc_tag_union = fmt_jsdoc_union(variant_types)
     self.emit(
         ' * @property {%s} .tag - Tag identifying the union variant.' %
         jsdoc_tag_union)
     self.emit(' */')
Exemple #3
0
    def _generate_custom_annotation_processors(self,
                                               ns,
                                               data_type,
                                               extra_annotations=()):
        """
        Generates code that will run a custom function 'processor' on every
        field with a custom annotation, no matter how deep (recursively) it
        might be located in data_type (incl. in elements of lists or maps).
        If extra_annotations is passed, it's assumed to be a list of custom
        annotation applied directly onto data_type (e.g. because it's a field
        in a struct).
        Yields pairs of (annotation_type, code) where code is code that
        evaluates to a function that should be executed with an instance of
        data_type as the only parameter, and whose return value should replace
        that instance.
        """
        # annotations applied to members of this type
        dt, _, _ = unwrap(data_type)
        if is_struct_type(dt) or is_union_type(dt):
            annotation_types_seen = set()
            for annotation in get_custom_annotations_recursive(dt):
                if annotation.annotation_type not in annotation_types_seen:
                    yield (annotation.annotation_type,
                           generate_func_call(
                               'bb.make_struct_annotation_processor',
                               args=[
                                   class_name_for_annotation_type(
                                       annotation.annotation_type, ns),
                                   'processor'
                               ]))
                    annotation_types_seen.add(annotation.annotation_type)
        elif is_list_type(dt):
            for annotation_type, recursive_processor in self._generate_custom_annotation_processors(
                    ns, dt.data_type):
                # every member needs to be replaced---use handwritten processor
                yield (annotation_type,
                       generate_func_call('bb.make_list_annotation_processor',
                                          args=[recursive_processor]))
        elif is_map_type(dt):
            for annotation_type, recursive_processor in self._generate_custom_annotation_processors(
                    ns, dt.value_data_type):
                # every value needs to be replaced---use handwritten processor
                yield (annotation_type,
                       generate_func_call(
                           'bb.make_map_value_annotation_processor',
                           args=[recursive_processor]))

        # annotations applied directly to this type (through aliases or
        # passed in from the caller)
        for annotation in itertools.chain(
                get_custom_annotations_for_alias(data_type),
                extra_annotations):
            yield (annotation.annotation_type,
                   generate_func_call(
                       'bb.partially_apply',
                       args=[
                           'processor',
                           self._generate_custom_annotation_instance(
                               ns, annotation)
                       ]))
Exemple #4
0
    def _generate_struct(self, struct_type, extra_parameters=None, nameOverride=None):
        """
        Emits a JSDoc @typedef for a struct.
        """
        extra_parameters = extra_parameters if extra_parameters is not None else []
        self._emit_jsdoc_header(struct_type.doc)
        self.emit(
            ' * @typedef {Object} %s' % (
                nameOverride if nameOverride else fmt_type_name(struct_type)
            )
        )

        # Some structs can explicitly list their subtypes. These structs
        # have a .tag field that indicate which subtype they are.
        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)

                jsdoc_tag_union = fmt_jsdoc_union(tag_values)
                txt = '@property {%s} .tag - Tag identifying the subtype variant.' % \
                    jsdoc_tag_union
                self.emit_wrapped_text(txt)
            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:
                        txt = '@property {\'%s\'} [.tag] - Tag identifying ' \
                            'this subtype variant. This field is only ' \
                            'present when needed to discriminate ' \
                            'between multiple possible subtypes.' % \
                            subtype.name
                        self.emit_wrapped_text(txt)
                        break

        for param_name, param_type, param_docstring in extra_parameters:
            param_docstring = ' - %s' % param_docstring if param_docstring else ''
            self.emit_wrapped_text(
                '@property {%s} %s%s' % (
                    param_type,
                    param_name,
                    param_docstring,
                ),
                prefix=' * ',
            )

        # NOTE: JSDoc @typedef does not support inheritance. Using @class would be inappropriate,
        # since these are not nominal types backed by a constructor. Thus, we emit all_fields,
        # which includes fields on parent types.
        for field in struct_type.all_fields:
            field_doc = ' - ' + field.doc if field.doc else ''
            field_type, nullable, _ = unwrap(field.data_type)
            field_js_type = fmt_type(field_type)
            # Translate nullable types into optional properties.
            field_name = '[' + field.name + ']' if nullable else field.name
            self.emit_wrapped_text(
                '@property {%s} %s%s' % (
                    field_js_type,
                    field_name,
                    self.process_doc(field_doc, self._docf),
                ),
                prefix=' * ',
            )

        self.emit(' */')
Exemple #5
0
    def _generate_struct(self,
                         struct_type,
                         extra_parameters=None,
                         nameOverride=None):
        """
        Emits a JSDoc @typedef for a struct.
        """
        extra_parameters = extra_parameters if extra_parameters is not None else []
        self._emit_jsdoc_header(struct_type.doc)
        self.emit(
            ' * @typedef {Object} %s' %
            (nameOverride if nameOverride else fmt_type_name(struct_type)))

        # Some structs can explicitly list their subtypes. These structs
        # have a .tag field that indicate which subtype they are.
        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)

                jsdoc_tag_union = fmt_jsdoc_union(tag_values)
                txt = '@property {%s} .tag - Tag identifying the subtype variant.' % \
                    jsdoc_tag_union
                self.emit_wrapped_text(txt)
            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:
                        txt = '@property {\'%s\'} [.tag] - Tag identifying ' \
                            'this subtype variant. This field is only ' \
                            'present when needed to discriminate ' \
                            'between multiple possible subtypes.' % \
                            subtype.name
                        self.emit_wrapped_text(txt)
                        break

        for param_name, param_type, param_docstring in extra_parameters:
            param_docstring = ' - %s' % param_docstring if param_docstring else ''
            self.emit_wrapped_text(
                '@property {%s} [%s]%s' % (
                    param_type,
                    param_name,
                    param_docstring,
                ),
                prefix=' * ',
            )

        # NOTE: JSDoc @typedef does not support inheritance. Using @class would be inappropriate,
        # since these are not nominal types backed by a constructor. Thus, we emit all_fields,
        # which includes fields on parent types.
        for field in struct_type.all_fields:
            field_doc = ' - ' + field.doc if field.doc else ''
            field_type, nullable, _ = unwrap(field.data_type)
            field_js_type = fmt_type(field_type)
            # Translate nullable types into optional properties.
            field_name = '[' + field.name + ']' if nullable else field.name
            self.emit_wrapped_text(
                '@property {%s} %s%s' % (
                    field_js_type,
                    field_name,
                    self.process_doc(field_doc, self._docf),
                ),
                prefix=' * ',
            )

        self.emit(' */')
 def _impl_serde_for_union(self, union):
     type_name = self.enum_name(union)
     with self._impl_deserialize(type_name):
         self.emit(u'// union deserializer')
         self.emit(u'use serde::de::{self, MapAccess, Visitor};')
         self.emit(u'struct EnumVisitor;')
         with self.block(u'impl<\'de> Visitor<\'de> for EnumVisitor'):
             self.emit(u'type Value = {};'.format(type_name))
             with self.emit_rust_function_def(
                     u'expecting',
                 [u'&self', u'f: &mut ::std::fmt::Formatter<\'_>'],
                     u'::std::fmt::Result'):
                 self.emit(u'f.write_str("a {} structure")'.format(
                     union.name))
             with self.emit_rust_function_def(
                     u'visit_map<V: MapAccess<\'de>>',
                 [u'self', u'mut map: V'],
                     u'Result<Self::Value, V::Error>'):
                 with self.block(u'let tag: &str = match map.next_key()?',
                                 after=';'):
                     self.emit(u'Some(".tag") => map.next_value()?,')
                     self.emit(
                         u'_ => return Err(de::Error::missing_field(".tag"))'
                     )
                 with self.block(u'match tag'):
                     for field in union.all_fields:
                         if field.catch_all:
                             # Handle the 'Other' variant at the end.
                             continue
                         variant_name = self.enum_variant_name(field)
                         ultimate_type = ir.unwrap(field.data_type)[0]
                         if isinstance(field.data_type, ir.Void):
                             with self.block(u'"{}" =>'.format(field.name)):
                                 self.emit(
                                     u'crate::eat_json_fields(&mut map)?;')
                                 self.emit(u'Ok({}::{})'.format(
                                     type_name, variant_name))
                         elif isinstance(ultimate_type, ir.Struct) \
                                 and not ultimate_type.has_enumerated_subtypes():
                             if isinstance(
                                     ir.unwrap_aliases(field.data_type)[0],
                                     ir.Nullable):
                                 # A nullable here means we might have more fields that can be
                                 # deserialized into the inner type, or we might have nothing,
                                 # meaning None.
                                 if not ultimate_type.all_required_fields:
                                     raise RuntimeError(
                                         '{}.{}: an optional struct with no'
                                         ' required fields is ambiguous'.
                                         format(union.name, field.name))
                                 self.emit(
                                     u'"{}" => Ok({}::{}({}::internal_deserialize_opt('
                                     u'map, true)?)),'.format(
                                         field.name, type_name,
                                         variant_name,
                                         self._rust_type(ultimate_type)))
                             else:
                                 self.emit(
                                     u'"{}" => Ok({}::{}({}::internal_deserialize(map)?)),'
                                     .format(
                                         field.name, type_name,
                                         variant_name,
                                         self._rust_type(field.data_type)))
                         else:
                             with self.block(u'"{}" =>'.format(field.name)):
                                 with self.block(u'match map.next_key()?'):
                                     self.emit(
                                         u'Some("{}") => Ok({}::{}(map.next_value()?)),'
                                         .format(field.name, type_name,
                                                 variant_name))
                                     if isinstance(
                                             ir.unwrap_aliases(
                                                 field.data_type)[0],
                                             ir.Nullable):
                                         # if it's null, the field can be omitted entirely
                                         self.emit(
                                             u'None => Ok({}::{}(None)),'.
                                             format(type_name,
                                                    variant_name))
                                     else:
                                         self.emit(
                                             u'None => Err('
                                             u'de::Error::missing_field("{}")),'
                                             .format(field.name))
                                     self.emit(
                                         u'_ => Err(de::Error::unknown_field('
                                         u'tag, VARIANTS))')
                     if not union.closed:
                         with self.block(u'_ =>'):
                             self.emit(
                                 u'crate::eat_json_fields(&mut map)?;')
                             self.emit(u'Ok({}::Other)'.format(type_name))
                     else:
                         self.emit(
                             u'_ => Err(de::Error::unknown_variant(tag, VARIANTS))'
                         )
         self.generate_multiline_list(
             list(u'"{}"'.format(field.name) for field in union.all_fields),
             before='const VARIANTS: &[&str] = &',
             after=';',
             delim=(u'[', u']'),
         )
         self.emit(
             u'deserializer.deserialize_struct("{}", VARIANTS, EnumVisitor)'
             .format(union.name))
     self.emit()
     with self._impl_serialize(type_name):
         self.emit(u'// union serializer')
         if len(union.all_fields) == 1 and union.all_fields[0].catch_all:
             # special case: an open union with no variants defined.
             self.emit(u'#![allow(unused_variables)]')
             self.emit(
                 u'Err(::serde::ser::Error::custom("cannot serialize an open union with '
                 u'no defined variants"))')
         else:
             self.emit(u'use serde::ser::SerializeStruct;')
             with self.block(u'match *self'):
                 for field in union.all_fields:
                     if field.catch_all:
                         # Handle the 'Other' variant at the end.
                         continue
                     variant_name = self.enum_variant_name(field)
                     if isinstance(field.data_type, ir.Void):
                         with self.block(u'{}::{} =>'.format(
                                 type_name, variant_name)):
                             self.emit(u'// unit')
                             self.emit(
                                 u'let mut s = serializer.serialize_struct("{}", 1)?;'
                                 .format(union.name))
                             self.emit(u's.serialize_field(".tag", "{}")?;'.
                                       format(field.name))
                             self.emit(u's.end()')
                     else:
                         ultimate_type = ir.unwrap(field.data_type)[0]
                         needs_x = not (isinstance(field.data_type,
                                                   ir.Struct)
                                        and not field.data_type.all_fields)
                         ref_x = 'ref x' if needs_x else '_'
                         with self.block(u'{}::{}({}) =>'.format(
                                 type_name, variant_name, ref_x)):
                             if self.is_enum_type(ultimate_type):
                                 # Inner type is a union or polymorphic struct; need to always
                                 # emit another nesting level.
                                 self.emit(
                                     u'// union or polymporphic struct')
                                 self.emit(
                                     u'let mut s = serializer.serialize_struct("{}", 2)?;'
                                     .format(union.name))
                                 self.emit(
                                     u's.serialize_field(".tag", "{}")?;'.
                                     format(field.name))
                                 self.emit(
                                     u's.serialize_field("{}", x)?;'.format(
                                         field.name))
                                 self.emit(u's.end()')
                             elif isinstance(
                                     ir.unwrap_aliases(field.data_type)[0],
                                     ir.Nullable):
                                 self.emit(
                                     u'// nullable (struct or primitive)')
                                 # If it's nullable and the value is None, just emit the tag and
                                 # nothing else, otherwise emit the fields directly at the same
                                 # level.
                                 num_fields = 1 if ir.is_primitive_type(ultimate_type) \
                                     else len(ultimate_type.all_fields) + 1
                                 self.emit(
                                     u'let n = if x.is_some() {{ {} }} else {{ 1 }};'
                                     .format(num_fields + 1))
                                 self.emit(
                                     u'let mut s = serializer.serialize_struct("{}", n)?;'
                                     .format(union.name))
                                 self.emit(
                                     u's.serialize_field(".tag", "{}")?;'.
                                     format(field.name))
                                 with self.block(u'if let Some(ref x) = x'):
                                     if ir.is_primitive_type(ultimate_type):
                                         self.emit(
                                             u's.serialize_field("{}", &x)?;'
                                             .format(field.name))
                                     else:
                                         self.emit(
                                             u'x.internal_serialize::<S>(&mut s)?;'
                                         )
                                 self.emit(u's.end()')
                             elif isinstance(ultimate_type, ir.Struct):
                                 self.emit(u'// struct')
                                 self.emit(
                                     u'let mut s = serializer.serialize_struct("{}", {})?;'
                                     .format(
                                         union.name,
                                         len(ultimate_type.all_fields) + 1))
                                 self.emit(
                                     u's.serialize_field(".tag", "{}")?;'.
                                     format(field.name))
                                 if ultimate_type.all_fields:
                                     self.emit(
                                         u'x.internal_serialize::<S>(&mut s)?;'
                                     )
                                 self.emit(u's.end()')
                             else:
                                 self.emit(u'// primitive')
                                 self.emit(
                                     u'let mut s = serializer.serialize_struct("{}", 2)?;'
                                     .format(union.name))
                                 self.emit(
                                     u's.serialize_field(".tag", "{}")?;'.
                                     format(field.name))
                                 self.emit(
                                     u's.serialize_field("{}", x)?;'.format(
                                         field.name))
                                 self.emit(u's.end()')
                 if not union.closed:
                     self.emit(
                         u'{}::Other => Err(::serde::ser::Error::custom('
                         u'"cannot serialize \'Other\' variant"))'.format(
                             type_name))
     self.emit()