fn selections_to_babel()

in compiler/crates/relay-typegen/src/lib.rs [799:952]


    fn selections_to_babel(
        &mut self,
        selections: impl Iterator<Item = TypeSelection>,
        unmasked: bool,
        fragment_type_name: Option<StringKey>,
    ) -> AST {
        let mut base_fields: TypeSelectionMap = Default::default();
        let mut by_concrete_type: IndexMap<Type, Vec<TypeSelection>> = Default::default();

        for selection in selections {
            if let Some(concrete_type) = selection.get_enclosing_concrete_type() {
                by_concrete_type
                    .entry(concrete_type)
                    .or_insert_with(Vec::new)
                    .push(selection);
            } else {
                let key = selection.get_string_key();
                let key = TypeSelectionKey {
                    key,
                    concrete_type: None,
                };
                match base_fields.entry(key) {
                    Entry::Occupied(entry) => {
                        let previous_sel = entry.get().clone();
                        *entry.into_mut() = merge_selection(Some(selection), previous_sel, true);
                    }
                    Entry::Vacant(entry) => {
                        entry.insert(selection);
                    }
                }
            }
        }

        let mut types: Vec<Vec<Prop>> = Vec::new();

        fn has_typename_selection(selections: &Vec<TypeSelection>) -> bool {
            selections.iter().any(TypeSelection::is_typename)
        }

        if !by_concrete_type.is_empty()
            && base_fields.values().all(TypeSelection::is_typename)
            && (base_fields.values().any(TypeSelection::is_typename)
                || by_concrete_type.values().all(has_typename_selection))
        {
            let mut typename_aliases = IndexSet::new();
            for (concrete_type, selections) in by_concrete_type {
                types.push(
                    group_refs(
                        base_fields.values().cloned().chain(selections),
                        self.should_sort_typegen_items,
                    )
                    .map(|selection| {
                        if selection.is_typename() {
                            typename_aliases.insert(selection.get_field_name_or_alias().expect(
                                "Just checked this exists by checking that the field is typename",
                            ));
                        }
                        self.make_prop(selection, unmasked, Some(concrete_type))
                    })
                    .collect(),
                );
            }

            // It might be some other type then the listed concrete types. Ideally, we
            // would set the type to diff(string, set of listed concrete types), but
            // this doesn't exist in Flow at the time.
            types.push(
                typename_aliases
                    .iter()
                    .map(|typename_alias| {
                        Prop::KeyValuePair(KeyValuePairProp {
                            key: *typename_alias,
                            read_only: true,
                            optional: false,
                            value: AST::OtherTypename,
                        })
                    })
                    .collect(),
            );
        } else {
            let mut selection_map = selections_to_map(hashmap_into_values(base_fields), false);
            for concrete_type_selections in hashmap_into_values(by_concrete_type) {
                merge_selections(
                    &mut selection_map,
                    selections_to_map(
                        concrete_type_selections.into_iter().map(|mut sel| {
                            sel.set_conditional(true);
                            sel
                        }),
                        false,
                    ),
                    true,
                );
            }
            let selection_map_values = group_refs(
                hashmap_into_values(selection_map),
                self.should_sort_typegen_items,
            )
            .map(|sel| {
                if let TypeSelection::ScalarField(ref scalar_field) = sel {
                    if sel.is_typename() {
                        if let Some(type_condition) = scalar_field.concrete_type {
                            let mut scalar_field = scalar_field.clone();
                            scalar_field.conditional = false;
                            return self.make_prop(
                                TypeSelection::ScalarField(scalar_field),
                                unmasked,
                                Some(type_condition),
                            );
                        }
                    }
                } else if let TypeSelection::LinkedField(ref linked_field) = sel {
                    if let Some(concrete_type) = linked_field.concrete_type {
                        let mut linked_field = linked_field.clone();
                        linked_field.concrete_type = None;
                        return self.make_prop(
                            TypeSelection::LinkedField(linked_field),
                            unmasked,
                            Some(concrete_type),
                        );
                    }
                }

                self.make_prop(sel, unmasked, None)
            })
            .collect();
            types.push(selection_map_values);
        }

        AST::Union(SortedASTList::new(
            types
                .into_iter()
                .map(|mut props: Vec<Prop>| {
                    if let Some(fragment_type_name) = fragment_type_name {
                        props.push(Prop::KeyValuePair(KeyValuePairProp {
                            key: *KEY_FRAGMENT_TYPE,
                            optional: false,
                            read_only: true,
                            value: AST::FragmentReferenceType(fragment_type_name),
                        }));
                    }
                    if unmasked {
                        AST::InexactObject(InexactObject::new(
                            props,
                            self.should_sort_typegen_items,
                        ))
                    } else {
                        AST::ExactObject(ExactObject::new(props, self.should_sort_typegen_items))
                    }
                })
                .collect(),
            self.should_sort_typegen_items,
        ))
    }