You are an expert in writing code in many different languages. Your goal is to perform code completion for the following code, keeping in mind the rest of the code and the file meta data.
    Metadata details:
    filename: analyze.rs
    path of file: ./repos/grafbase/cli/crates/typed-resolvers/src
    
    the code of the file until where you have to start completion: use engine_parser::types as ast;
use engine_value::ConstValue;
use std::{collections::HashMap, ops, str::FromStr};

#[must_use]
pub fn analyze(graphql_document: &ast::ServiceDocument) -> AnalyzedSchema<'_> {
    let mut schema = AnalyzedSchema::default();

    analyze_top_level(graphql_document, &mut schema);
    analyze_fields(graphql_document, &mut schema);

    schema
}

/// First pass. Resolve what definitions exist in the schema (objects, unions, etc.).
fn analyze_top_level<'doc>(graphql_document: &'doc ast::ServiceDocument, schema: &mut AnalyzedSchema<'doc>) {
    for definition in &graphql_document.definitions {
        match definition {
            ast::TypeSystemDefinition::Type(ty) => {
                if ty.node.extend {
                    // We ignore type extensions in the first pass because we can't know if they extend
                    // types defined in the document yet.
                    continue;
                }
                let name = &ty.node.name.node;
                let docs = ty.node.description.as_ref().map(|desc| desc.node.as_str());

                match &ty.node.kind {
                    ast::TypeKind::Scalar => {
                        schema.push_custom_scalar(CustomScalar { name, docs });
                    }
                    ast::TypeKind::Object(_) => {
                        schema.push_output_type(Object {
                            name,
                            docs,
                            kind: ObjectKind::Object,
                        });
                    }
                    ast::TypeKind::Interface(_) => {
                        schema.push_output_type(Object {
                            name,
                            docs,
                            kind: ObjectKind::Interface,
                        });
                    }
                    ast::TypeKind::InputObject(_) => {
                        schema.push_output_type(Object {
                            name,
                            docs,
                            kind: ObjectKind::InputObject,
                        });
                    }
                    ast::TypeKind::Union(_) => {
                        schema.push_union(Union { name });
                    }
                    ast::TypeKind::Enum(r#enum) => {
                        let id = schema.push_enum(Enum { name, docs });

                        for variant in &r#enum.values {
                            schema.push_enum_variant(id, &variant.node.value.node);
                        }
                    }
                }
            }
            ast::TypeSystemDefinition::Schema(_) | ast::TypeSystemDefinition::Directive(_) => (), // not interested
        }
    }

    for name in ["Query", "Mutation", "Subscription"] {
        if !schema.definition_names.contains_key(name) {
            schema.push_output_type(Object {
                name,
                docs: None,
                kind: ObjectKind::Object,
            });
        }
    }
}

/// Second pass. We know about all definitions, now we analyze fields inside object and interface
/// types, and variants inside unions.
fn analyze_fields<'doc>(graphql_document: &'doc ast::ServiceDocument, schema: &mut AnalyzedSchema<'doc>) {
    for definition in &
    
fn analyze_fields<'doc>(graphql_document: &'doc ast::ServiceDocument, schema: &mut AnalyzedSchema<'doc>) {
    for definition in &graphql_document.definitions {
        match definition {
            ast::TypeSystemDefinition::Type(ty) => {
                let name = ty.node.name.node.as_str();
                match &ty.node.kind {
                    ast::TypeKind::Union(union_definition) => {
                        let Some(Definition::Union(union_id)) = schema.definition_names.get(name).copied() else {
                            continue;
                        };

                        for variant_name in &union_definition.members {
                            match schema.definition_names.get(variant_name.node.as_str()) {
                                Some(Definition::Object(object_id)) => schema.push_union_variant(union_id, *object_id),
                                None | Some(_) => (), // invalid: union variant is not an object name. Ignore here.
                            }
                        }
                    }
                    ast::TypeKind::Object(object_definition) => {
                        let Some(Definition::Object(object_id)) = schema.definition_names.get(name).copied() else {
                            continue;
                        };

                        for ast_field in &object_definition.fields {
                            if let Some(field) = analyze_ast_field(&ast_field.node, schema) {
                                let field_id = schema.push_object_field(object_id, field);
                                for arg in &ast_field.node.arguments {
                                    if let Some(r#type) = GraphqlType::resolve(&arg.node.ty.node, schema) {
                                        schema.object_field_args.push((
                                            field_id,
                                            FieldArgument {
                                                name: arg.node.name.node.as_str(),
                                                r#type,
                                            },
                                        ));
                                    }
                                }
                            }
                        }
                    }
                    ast::TypeKind::Interface(iface_definition) => {
                        let Some(Definition::Object(object_id)) = schema.definition_names.get(name).copied() else {
                            continue;
                        };

                        for field in &iface_definition.fields {
                            if let Some(field) = analyze_ast_field(&field.node, schema) {
                                schema.object_fields.push((object_id, field));
                            }
                        }
                    }
                    ast::TypeKind::InputObject(input_object_definition) => {
                        let Some(Definition::Object(object_id)) = schema.definition_names.get(name).copied() else {
                            continue;
                        };

                        for field in &input_object_definition.fields {
                            if let Some(field) = analyze_ast_input_field(&field.node, schema) {
                                schema.object_fields.push((object_id, field));
                            }
                        }
                    }
                    // Already completely handled in first pass.
                    ast::TypeKind::Scalar | ast::TypeKind::Enum(_) => (),
                }
            }

            ast::TypeSystemDefinition::Schema(_) | ast::TypeSystemDefinition::Directive(_) => (), // not interested
        }
    }

    schema.object_fields.sort_by_key(|(object_id, _)| *object_id);
    schema.object_field_args.sort_by_key(|(object_id, _)| *object_id);
}