<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use common::{DiagnosticsResult, PerfLogEvent, PerfLogger};
use fnv::FnvHashSet;
use graphql_ir::Program;
use interner::StringKey;
use relay_transforms::*;
use std::sync::Arc;

use super::log_program_stats::print_stats;

#[derive(Debug)]
pub struct Programs {
    pub source: Arc<Program>,
    pub reader: Arc<Program>,
    pub normalization: Arc<Program>,
    pub operation_text: Arc<Program>,
    pub typegen: Arc<Program>,
}

pub fn apply_transforms<TPerfLogger>(
    project_name: StringKey,
    program: Arc<Program>,
    base_fragment_names: Arc<FnvHashSet<StringKey>>,
    connection_interface: &ConnectionInterface,
    feature_flags: Arc<FeatureFlags>,
    perf_logger: Arc<TPerfLogger>,
) -> DiagnosticsResult<Programs>
where
    TPerfLogger: PerfLogger + 'static,
{
    // The execution pipeline is as follows, where items at the same indentation
    // can be computed independently and therefore in parallel:
    // |- common
    //  |- reader
    //  |- operation
    //     |- normalization
    //     |- operation_text
    // |- typegen
    //
    // NOTE: try_join(f1, f2) prefers the errors from f1 over f2, so process the normalization
    // program first since it is likely to include more errors (both per-fragment and
    // whole-operation errors) than the reader_program which just includes per-fragment errors.
    let (((normalization_program, text_program), reader_program), typegen_program) = try_join(
        || {
            let common_program = apply_common_transforms(
                project_name,
                Arc::clone(&program),
                connection_interface,
                Arc::clone(&feature_flags),
                Arc::clone(&base_fragment_names),
                Arc::clone(&perf_logger),
            )?;

            try_join(
                || {
                    let operation_program = apply_operation_transforms(
                        project_name,
                        Arc::clone(&common_program),
                        connection_interface,
                        Arc::clone(&base_fragment_names),
                        Arc::clone(&perf_logger),
                    )?;

                    try_join(
                        || {
                            apply_normalization_transforms(
                                project_name,
                                Arc::clone(&operation_program),
                                Arc::clone(&perf_logger),
                            )
                        },
                        || {
                            apply_operation_text_transforms(
                                project_name,
                                Arc::clone(&operation_program),
                                Arc::clone(&perf_logger),
                            )
                        },
                    )
                },
                || {
                    apply_reader_transforms(
                        project_name,
                        Arc::clone(&common_program),
                        Arc::clone(&feature_flags),
                        Arc::clone(&base_fragment_names),
                        Arc::clone(&perf_logger),
                    )
                },
            )
        },
        || {
            apply_typegen_transforms(
                project_name,
                Arc::clone(&program),
                Arc::clone(&feature_flags),
                Arc::clone(&base_fragment_names),
                Arc::clone(&perf_logger),
            )
        },
    )?;

    Ok(Programs {
        source: program,
        reader: reader_program,
        normalization: normalization_program,
        operation_text: text_program,
        typegen: typegen_program,
    })
}

/// Applies transforms that apply to every output.
fn apply_common_transforms(
    project_name: StringKey,
    program: Arc<Program>,
    connection_interface: &ConnectionInterface,
    feature_flags: Arc<FeatureFlags>,
    base_fragment_names: Arc<FnvHashSet<StringKey>>,
    perf_logger: Arc<impl PerfLogger>,
) -> DiagnosticsResult<Arc<Program>> {
    // JS compiler
    // * DisallowIdAsAlias (in validate)
    // + ConnectionTransform
    // * RelayDirectiveTransform (in validate)
    // + MaskTransform
    // + MatchTransform
    // + RefetchableFragmentTransform
    // + DeferStreamTransform
    let log_event = perf_logger.create_event("apply_common_transforms");
    log_event.string("project", project_name.to_string());
    let program = log_event.time("transform_connections", || {
        transform_connections(&program, connection_interface)
    });
    let program = log_event.time("mask", || mask(&program));
    let program = log_event.time("transform_defer_stream", || {
        transform_defer_stream(&program)
    })?;
    let program = log_event.time("transform_match", || transform_match(&program))?;
    let program = log_event.time("transform_refetchable_fragment", || {
        transform_refetchable_fragment(&program, &base_fragment_names, false)
    })?;
    let program = if feature_flags.enable_flight_transform {
        log_event.time("react_flight", || react_flight(&program))?
    } else {
        program
    };
    perf_logger.complete_event(log_event);

    Ok(Arc::new(program))
}

/// Applies transforms only for generated reader code.
/// Corresponds to the "fragment transforms" in the JS compiler.
fn apply_reader_transforms(
    project_name: StringKey,
    program: Arc<Program>,
    feature_flags: Arc<FeatureFlags>,
    base_fragment_names: Arc<FnvHashSet<StringKey>>,
    perf_logger: Arc<impl PerfLogger>,
) -> DiagnosticsResult<Arc<Program>> {
    // JS compiler
    // + ClientExtensionsTransform
    // + FieldHandleTransform
    // + InlineDataFragmentTransform
    // + FlattenTransform, flattenAbstractTypes: true
    // + SkipRedundantNodesTransform
    let log_event = perf_logger.create_event("apply_reader_transforms");
    log_event.string("project", project_name.to_string());
    let program = log_event.time("required_directive", || {
        required_directive(&program, &feature_flags)
    })?;

    let program = log_event.time("client_extensions", || client_extensions(&program));
    let program = log_event.time("handle_field_transform", || {
        handle_field_transform(&program)
    });
    let program = log_event.time("inline_data_fragment", || inline_data_fragment(&program))?;
    let program = log_event.time("remove_base_fragments", || {
        remove_base_fragments(&program, base_fragment_names)
    });
    let program = log_event.time("flatten", || flatten(&program, true))?;
    let program = log_event.time("skip_redundant_nodes", || skip_redundant_nodes(&program));
    let program = log_event.time("generate_data_driven_dependency_metadata", || {
        generate_data_driven_dependency_metadata(&program)
    });

    perf_logger.complete_event(log_event);

    Ok(Arc::new(program))
}

/// Applies transforms that apply to all operation artifacts.
/// Corresponds to the "query transforms" in the JS compiler.
fn apply_operation_transforms(
    project_name: StringKey,
    program: Arc<Program>,
    connection_interface: &ConnectionInterface,
    base_fragment_names: Arc<FnvHashSet<StringKey>>,
    perf_logger: Arc<impl PerfLogger>,
) -> DiagnosticsResult<Arc<Program>> {
    // JS compiler
    // + SplitModuleImportTransform
    // * ValidateUnusedVariablesTransform (Moved to common_transforms)
    // + ApplyFragmentArgumentTransform
    // + ValidateGlobalVariablesTransform
    // + GenerateIDFieldTransform
    // * TestOperationTransform - part of relay_codegen
    let log_event = perf_logger.create_event("apply_operation_transforms");
    log_event.string("project", project_name.to_string());

    let program = log_event.time("split_module_import", || {
        split_module_import(&program, &base_fragment_names)
    });
    let program = log_event.time("apply_fragment_arguments", || {
        apply_fragment_arguments(&program)
    })?;
    log_event.time("validate_global_variables", || {
        validate_global_variables(&program)
    })?;
    let program = log_event.time("generate_id_field", || generate_id_field(&program));
    let program = log_event.time("declarative_connection", || {
        transform_declarative_connection(&program, connection_interface)
    })?;

    // TODO(T67052528): execute FB-specific transforms only if config options is provided
    let program = log_event.time("generate_subscription_name_metadata", || {
        generate_subscription_name_metadata(&program)
    })?;
    let program = log_event.time("generate_live_query_metadata", || {
        generate_live_query_metadata(&program)
    })?;

    perf_logger.complete_event(log_event);

    Ok(Arc::new(program))
}

/// After the operation transforms, this applies further transforms that only
/// apply to the generated normalization code.
///
/// Corresponds to the "codegen transforms" in the JS compiler
fn apply_normalization_transforms(
    project_name: StringKey,
    program: Arc<Program>,
    perf_logger: Arc<impl PerfLogger>,
) -> DiagnosticsResult<Arc<Program>> {
    // JS compiler
    // + SkipUnreachableNodeTransform
    // + InlineFragmentsTransform
    // + ClientExtensionsTransform
    // + GenerateTypeNameTransform
    // + FlattenTransform, flattenAbstractTypes: true
    // + SkipRedundantNodesTransform
    let log_event = perf_logger.create_event("apply_normalization_transforms");
    log_event.string("project", project_name.to_string());

    print_stats("normalization start", &program);
    let program = log_event.time("relay_early_flush", || relay_early_flush(&program))?;
    print_stats("relay_early_flush", &program);

    let program = log_event.time("skip_unreachable_node", || skip_unreachable_node(&program));
    print_stats("skip_unreachable_node", &program);

    let program = log_event.time("inline_fragments", || inline_fragments(&program));
    print_stats("inline_fragments", &program);

    let program = log_event.time("client_extensions", || client_extensions(&program));
    print_stats("client_extensions", &program);

    let program = log_event.time("generate_typename", || generate_typename(&program, true));
    print_stats("generate_typename", &program);

    let program = log_event.time("flatten", || flatten(&program, true))?;
    print_stats("flatten", &program);

    let program = log_event.time("skip_redundant_nodes", || skip_redundant_nodes(&program));
    print_stats("skip_redundant_nodes", &program);

    let program = log_event.time("generate_test_operation_metadata", || {
        generate_test_operation_metadata(&program)
    });
    print_stats("generate_test_operation_metadata", &program);

    perf_logger.complete_event(log_event);

    Ok(Arc::new(program))
}

/// After the operation transforms, this applies further transforms that only
/// apply to the printed operation text.
///
/// Corresponds to the "print transforms" in the JS compiler
fn apply_operation_text_transforms(
    project_name: StringKey,
    program: Arc<Program>,
    perf_logger: Arc<impl PerfLogger>,
) -> DiagnosticsResult<Arc<Program>> {
    // JS compiler
    // + SkipSplitOperationTransform
    // * ClientExtensionsTransform (not necessary in rust)
    // + SkipClientExtensionsTransform
    // + SkipUnreachableNodeTransform
    // + GenerateTypeNameTransform
    // + FlattenTransform, flattenAbstractTypes: false
    // * SkipHandleFieldTransform (not necessary in rust)
    // + FilterDirectivesTransform
    // + SkipUnusedVariablesTransform
    // + ValidateRequiredArgumentsTransform
    let log_event = perf_logger.create_event("apply_operation_text_transforms");
    log_event.string("project", project_name.to_string());

    let program = log_event.time("relay_early_flush", || relay_early_flush(&program))?;
    let program = log_event.time("skip_split_operation", || skip_split_operation(&program));
    let program = log_event.time("skip_client_extensions", || {
        skip_client_extensions(&program)
    });
    let program = log_event.time("skip_unreachable_node", || skip_unreachable_node(&program));
    let program = log_event.time("generate_typename", || generate_typename(&program, false));
    let program = log_event.time("flatten", || flatten(&program, false))?;
    let program = log_event.time("skip_unused_variables", || skip_unused_variables(&program));
    let program = log_event.time("skip_client_directives", || {
        skip_client_directives(&program)
    });
    log_event.time("validate_required_arguments", || {
        validate_required_arguments(&program)
    })?;
    let program = log_event.time("unwrap_custom_directive_selection", || {
        unwrap_custom_directive_selection(&program)
    });
    perf_logger.complete_event(log_event);

    Ok(Arc::new(program))
}

fn apply_typegen_transforms(
    project_name: StringKey,
    program: Arc<Program>,
    feature_flags: Arc<FeatureFlags>,
    base_fragment_names: Arc<FnvHashSet<StringKey>>,
    perf_logger: Arc<impl PerfLogger>,
) -> DiagnosticsResult<Arc<Program>> {
    // JS compiler
    // * RelayDirectiveTransform
    // + MaskTransform
    // + MatchTransform
    // + FlattenTransform, flattenAbstractTypes: false
    // + RefetchableFragmentTransform,
    let log_event = perf_logger.create_event("apply_typegen_transforms");
    log_event.string("project", project_name.to_string());

    let program = log_event.time("mask", || mask(&program));
    let program = log_event.time("transform_match", || transform_match(&program))?;
    let program = log_event.time("required_directive", || {
        required_directive(&program, &feature_flags)
    })?;
    let program = log_event.time("flatten", || flatten(&program, false))?;
    let program = log_event.time("transform_refetchable_fragment", || {
        transform_refetchable_fragment(&program, &base_fragment_names, true)
    })?;
    let program = log_event.time("remove_base_fragments", || {
        remove_base_fragments(&program, base_fragment_names)
    });
    perf_logger.complete_event(log_event);

    Ok(Arc::new(program))
}

fn try_join<T1, F1, T2, F2, E>(f1: F1, f2: F2) -> Result<(T1, T2), Vec<E>>
where
    F1: FnOnce() -> Result<T1, Vec<E>> + Send,
    F2: FnOnce() -> Result<T2, Vec<E>> + Send,
    T1: Send,
    T2: Send,
    E: Send,
{
    let (v1, v2) = rayon::join(f1, f2);
    Ok((v1?, v2?))
}
<commit_msg>Avoid shadowing programs in apply_transforms<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use common::{DiagnosticsResult, PerfLogEvent, PerfLogger};
use fnv::FnvHashSet;
use graphql_ir::Program;
use interner::StringKey;
use relay_transforms::*;
use std::sync::Arc;

use super::log_program_stats::print_stats;

#[derive(Debug)]
pub struct Programs {
    pub source: Arc<Program>,
    pub reader: Arc<Program>,
    pub normalization: Arc<Program>,
    pub operation_text: Arc<Program>,
    pub typegen: Arc<Program>,
}

pub fn apply_transforms<TPerfLogger>(
    project_name: StringKey,
    program: Arc<Program>,
    base_fragment_names: Arc<FnvHashSet<StringKey>>,
    connection_interface: &ConnectionInterface,
    feature_flags: Arc<FeatureFlags>,
    perf_logger: Arc<TPerfLogger>,
) -> DiagnosticsResult<Programs>
where
    TPerfLogger: PerfLogger + 'static,
{
    // The execution pipeline is as follows, where items at the same indentation
    // can be computed independently and therefore in parallel:
    // |- common
    //  |- reader
    //  |- operation
    //     |- normalization
    //     |- operation_text
    // |- typegen
    //
    // NOTE: try_join(f1, f2) prefers the errors from f1 over f2, so process the normalization
    // program first since it is likely to include more errors (both per-fragment and
    // whole-operation errors) than the reader_program which just includes per-fragment errors.
    let (((normalization_program, text_program), reader_program), typegen_program) = try_join(
        || {
            let common_program = apply_common_transforms(
                project_name,
                Arc::clone(&program),
                connection_interface,
                Arc::clone(&feature_flags),
                Arc::clone(&base_fragment_names),
                Arc::clone(&perf_logger),
            )?;

            try_join(
                || {
                    let operation_program = apply_operation_transforms(
                        project_name,
                        Arc::clone(&common_program),
                        connection_interface,
                        Arc::clone(&base_fragment_names),
                        Arc::clone(&perf_logger),
                    )?;

                    try_join(
                        || {
                            apply_normalization_transforms(
                                project_name,
                                Arc::clone(&operation_program),
                                Arc::clone(&perf_logger),
                            )
                        },
                        || {
                            apply_operation_text_transforms(
                                project_name,
                                Arc::clone(&operation_program),
                                Arc::clone(&perf_logger),
                            )
                        },
                    )
                },
                || {
                    apply_reader_transforms(
                        project_name,
                        Arc::clone(&common_program),
                        Arc::clone(&feature_flags),
                        Arc::clone(&base_fragment_names),
                        Arc::clone(&perf_logger),
                    )
                },
            )
        },
        || {
            apply_typegen_transforms(
                project_name,
                Arc::clone(&program),
                Arc::clone(&feature_flags),
                Arc::clone(&base_fragment_names),
                Arc::clone(&perf_logger),
            )
        },
    )?;

    Ok(Programs {
        source: program,
        reader: reader_program,
        normalization: normalization_program,
        operation_text: text_program,
        typegen: typegen_program,
    })
}

/// Applies transforms that apply to every output.
fn apply_common_transforms(
    project_name: StringKey,
    program: Arc<Program>,
    connection_interface: &ConnectionInterface,
    feature_flags: Arc<FeatureFlags>,
    base_fragment_names: Arc<FnvHashSet<StringKey>>,
    perf_logger: Arc<impl PerfLogger>,
) -> DiagnosticsResult<Arc<Program>> {
    // JS compiler
    // * DisallowIdAsAlias (in validate)
    // + ConnectionTransform
    // * RelayDirectiveTransform (in validate)
    // + MaskTransform
    // + MatchTransform
    // + RefetchableFragmentTransform
    // + DeferStreamTransform
    let log_event = perf_logger.create_event("apply_common_transforms");
    log_event.string("project", project_name.to_string());
    let mut program = log_event.time("transform_connections", || {
        transform_connections(&program, connection_interface)
    });
    program = log_event.time("mask", || mask(&program));
    program = log_event.time("transform_defer_stream", || {
        transform_defer_stream(&program)
    })?;
    program = log_event.time("transform_match", || transform_match(&program))?;
    program = log_event.time("transform_refetchable_fragment", || {
        transform_refetchable_fragment(&program, &base_fragment_names, false)
    })?;
    program = if feature_flags.enable_flight_transform {
        log_event.time("react_flight", || react_flight(&program))?
    } else {
        program
    };
    perf_logger.complete_event(log_event);

    Ok(Arc::new(program))
}

/// Applies transforms only for generated reader code.
/// Corresponds to the "fragment transforms" in the JS compiler.
fn apply_reader_transforms(
    project_name: StringKey,
    program: Arc<Program>,
    feature_flags: Arc<FeatureFlags>,
    base_fragment_names: Arc<FnvHashSet<StringKey>>,
    perf_logger: Arc<impl PerfLogger>,
) -> DiagnosticsResult<Arc<Program>> {
    // JS compiler
    // + ClientExtensionsTransform
    // + FieldHandleTransform
    // + InlineDataFragmentTransform
    // + FlattenTransform, flattenAbstractTypes: true
    // + SkipRedundantNodesTransform
    let log_event = perf_logger.create_event("apply_reader_transforms");
    log_event.string("project", project_name.to_string());
    let mut program = log_event.time("required_directive", || {
        required_directive(&program, &feature_flags)
    })?;

    program = log_event.time("client_extensions", || client_extensions(&program));
    program = log_event.time("handle_field_transform", || {
        handle_field_transform(&program)
    });
    program = log_event.time("inline_data_fragment", || inline_data_fragment(&program))?;
    program = log_event.time("remove_base_fragments", || {
        remove_base_fragments(&program, base_fragment_names)
    });
    program = log_event.time("flatten", || flatten(&program, true))?;
    program = log_event.time("skip_redundant_nodes", || skip_redundant_nodes(&program));
    program = log_event.time("generate_data_driven_dependency_metadata", || {
        generate_data_driven_dependency_metadata(&program)
    });

    perf_logger.complete_event(log_event);

    Ok(Arc::new(program))
}

/// Applies transforms that apply to all operation artifacts.
/// Corresponds to the "query transforms" in the JS compiler.
fn apply_operation_transforms(
    project_name: StringKey,
    program: Arc<Program>,
    connection_interface: &ConnectionInterface,
    base_fragment_names: Arc<FnvHashSet<StringKey>>,
    perf_logger: Arc<impl PerfLogger>,
) -> DiagnosticsResult<Arc<Program>> {
    // JS compiler
    // + SplitModuleImportTransform
    // * ValidateUnusedVariablesTransform (Moved to common_transforms)
    // + ApplyFragmentArgumentTransform
    // + ValidateGlobalVariablesTransform
    // + GenerateIDFieldTransform
    // * TestOperationTransform - part of relay_codegen
    let log_event = perf_logger.create_event("apply_operation_transforms");
    log_event.string("project", project_name.to_string());

    let mut program = log_event.time("split_module_import", || {
        split_module_import(&program, &base_fragment_names)
    });
    program = log_event.time("apply_fragment_arguments", || {
        apply_fragment_arguments(&program)
    })?;
    log_event.time("validate_global_variables", || {
        validate_global_variables(&program)
    })?;
    program = log_event.time("generate_id_field", || generate_id_field(&program));
    program = log_event.time("declarative_connection", || {
        transform_declarative_connection(&program, connection_interface)
    })?;

    // TODO(T67052528): execute FB-specific transforms only if config options is provided
    program = log_event.time("generate_subscription_name_metadata", || {
        generate_subscription_name_metadata(&program)
    })?;
    program = log_event.time("generate_live_query_metadata", || {
        generate_live_query_metadata(&program)
    })?;

    perf_logger.complete_event(log_event);

    Ok(Arc::new(program))
}

/// After the operation transforms, this applies further transforms that only
/// apply to the generated normalization code.
///
/// Corresponds to the "codegen transforms" in the JS compiler
fn apply_normalization_transforms(
    project_name: StringKey,
    program: Arc<Program>,
    perf_logger: Arc<impl PerfLogger>,
) -> DiagnosticsResult<Arc<Program>> {
    // JS compiler
    // + SkipUnreachableNodeTransform
    // + InlineFragmentsTransform
    // + ClientExtensionsTransform
    // + GenerateTypeNameTransform
    // + FlattenTransform, flattenAbstractTypes: true
    // + SkipRedundantNodesTransform
    let log_event = perf_logger.create_event("apply_normalization_transforms");
    log_event.string("project", project_name.to_string());
    print_stats("normalization start", &program);

    let mut program = log_event.time("relay_early_flush", || relay_early_flush(&program))?;
    print_stats("relay_early_flush", &program);

    program = log_event.time("skip_unreachable_node", || skip_unreachable_node(&program));
    print_stats("skip_unreachable_node", &program);

    program = log_event.time("inline_fragments", || inline_fragments(&program));
    print_stats("inline_fragments", &program);

    program = log_event.time("client_extensions", || client_extensions(&program));
    print_stats("client_extensions", &program);

    program = log_event.time("generate_typename", || generate_typename(&program, true));
    print_stats("generate_typename", &program);

    program = log_event.time("flatten", || flatten(&program, true))?;
    print_stats("flatten", &program);

    program = log_event.time("skip_redundant_nodes", || skip_redundant_nodes(&program));
    print_stats("skip_redundant_nodes", &program);

    program = log_event.time("generate_test_operation_metadata", || {
        generate_test_operation_metadata(&program)
    });
    print_stats("generate_test_operation_metadata", &program);

    perf_logger.complete_event(log_event);

    Ok(Arc::new(program))
}

/// After the operation transforms, this applies further transforms that only
/// apply to the printed operation text.
///
/// Corresponds to the "print transforms" in the JS compiler
fn apply_operation_text_transforms(
    project_name: StringKey,
    program: Arc<Program>,
    perf_logger: Arc<impl PerfLogger>,
) -> DiagnosticsResult<Arc<Program>> {
    // JS compiler
    // + SkipSplitOperationTransform
    // * ClientExtensionsTransform (not necessary in rust)
    // + SkipClientExtensionsTransform
    // + SkipUnreachableNodeTransform
    // + GenerateTypeNameTransform
    // + FlattenTransform, flattenAbstractTypes: false
    // * SkipHandleFieldTransform (not necessary in rust)
    // + FilterDirectivesTransform
    // + SkipUnusedVariablesTransform
    // + ValidateRequiredArgumentsTransform
    let log_event = perf_logger.create_event("apply_operation_text_transforms");
    log_event.string("project", project_name.to_string());

    let mut program = log_event.time("relay_early_flush", || relay_early_flush(&program))?;
    program = log_event.time("skip_split_operation", || skip_split_operation(&program));
    program = log_event.time("skip_client_extensions", || {
        skip_client_extensions(&program)
    });
    program = log_event.time("skip_unreachable_node", || skip_unreachable_node(&program));
    program = log_event.time("generate_typename", || generate_typename(&program, false));
    program = log_event.time("flatten", || flatten(&program, false))?;
    program = log_event.time("skip_unused_variables", || skip_unused_variables(&program));
    program = log_event.time("skip_client_directives", || {
        skip_client_directives(&program)
    });
    log_event.time("validate_required_arguments", || {
        validate_required_arguments(&program)
    })?;
    program = log_event.time("unwrap_custom_directive_selection", || {
        unwrap_custom_directive_selection(&program)
    });
    perf_logger.complete_event(log_event);

    Ok(Arc::new(program))
}

fn apply_typegen_transforms(
    project_name: StringKey,
    program: Arc<Program>,
    feature_flags: Arc<FeatureFlags>,
    base_fragment_names: Arc<FnvHashSet<StringKey>>,
    perf_logger: Arc<impl PerfLogger>,
) -> DiagnosticsResult<Arc<Program>> {
    // JS compiler
    // * RelayDirectiveTransform
    // + MaskTransform
    // + MatchTransform
    // + FlattenTransform, flattenAbstractTypes: false
    // + RefetchableFragmentTransform,
    let log_event = perf_logger.create_event("apply_typegen_transforms");
    log_event.string("project", project_name.to_string());

    let mut program = log_event.time("mask", || mask(&program));
    program = log_event.time("transform_match", || transform_match(&program))?;
    program = log_event.time("required_directive", || {
        required_directive(&program, &feature_flags)
    })?;
    program = log_event.time("flatten", || flatten(&program, false))?;
    program = log_event.time("transform_refetchable_fragment", || {
        transform_refetchable_fragment(&program, &base_fragment_names, true)
    })?;
    program = log_event.time("remove_base_fragments", || {
        remove_base_fragments(&program, base_fragment_names)
    });
    perf_logger.complete_event(log_event);

    Ok(Arc::new(program))
}

fn try_join<T1, F1, T2, F2, E>(f1: F1, f2: F2) -> Result<(T1, T2), Vec<E>>
where
    F1: FnOnce() -> Result<T1, Vec<E>> + Send,
    F2: FnOnce() -> Result<T2, Vec<E>> + Send,
    T1: Send,
    T2: Send,
    E: Send,
{
    let (v1, v2) = rayon::join(f1, f2);
    Ok((v1?, v2?))
}
<|endoftext|>


<commit_before>/// The validator service. Connects to a beacon node and signs blocks when required.
use crate::attester_service::{AttestationGrpcClient, AttesterService};
use crate::block_producer_service::{BeaconBlockGrpcClient, BlockProducerService};
use crate::config::Config as ValidatorConfig;
use crate::duties::{DutiesManager, DutiesManagerService, EpochDutiesMap};
use attester::test_utils::EpochMap;
use attester::{test_utils::LocalSigner as AttesterLocalSigner, Attester};
use block_proposer::{test_utils::LocalSigner as BlockProposerLocalSigner, BlockProducer};
use bls::Keypair;
use grpcio::{ChannelBuilder, EnvBuilder};
use protos::services::Empty;
use protos::services_grpc::{
    AttestationServiceClient, BeaconBlockServiceClient, BeaconNodeServiceClient,
    ValidatorServiceClient,
};
use slog::{debug, info, warn};
use slot_clock::{SlotClock, SystemTimeSlotClock};
use std::ops::Sub;
use std::sync::Arc;
use std::time::{Duration, Instant, SystemTime};
use tokio::prelude::*;
use tokio::runtime::Builder;
use tokio::timer::Interval;
use tokio_timer::clock::Clock;
use types::{Epoch, Fork, Slot};

//TODO: This service should be simplified in the future. Can be made more steamlined.

/// The validator service. This is the main thread that executes and maintains validator
/// duties.
pub struct Service {
    /// The node we currently connected to.
    connected_node_version: String,
    /// The chain id we are processing on.
    chain_id: u16,
    /// The fork state we processing on.
    fork: Fork,
    /// The slot clock keeping track of time.
    slot_clock: Arc<SystemTimeSlotClock>,
    /// The current slot we are processing.
    current_slot: Slot,
    /// Micro seconds until the next slot. This is used for initializing the tokio timer interval.
    micros_to_next_slot: Duration,
    // GRPC Clients
    /// The beacon block GRPC client.
    beacon_block_client: Arc<BeaconBlockServiceClient>,
    /// The validator GRPC client.
    validator_client: Arc<ValidatorServiceClient>,
    /// The attester GRPC client.
    attester_client: Arc<AttestationServiceClient>,
    /// The validator client logger.
    log: slog::Logger,
}

impl Service {
    ///  Initial connection to the beacon node to determine its properties.
    ///
    ///  This tries to connect to a beacon node. Once connected, it initialised the gRPC clients
    ///  and returns an instance of the service.
    fn initialize_service(config: &ValidatorConfig, log: slog::Logger) -> Self {
        // initialise the beacon node client to check for a connection

        let env = Arc::new(EnvBuilder::new().build());
        // Beacon node gRPC beacon node endpoints.
        let beacon_node_client = {
            let ch = ChannelBuilder::new(env.clone()).connect(&config.server);
            Arc::new(BeaconNodeServiceClient::new(ch))
        };

        // retrieve node information
        let node_info = loop {
            let info = match beacon_node_client.info(&Empty::new()) {
                Err(e) => {
                    warn!(log, "Could not connect to node. Error: {}", e);
                    info!(log, "Retrying in 5 seconds...");
                    std::thread::sleep(Duration::from_secs(5));
                    continue;
                }
                Ok(info) => break info,
            };
        };

        // build requisite objects to form Self
        let genesis_time = node_info.get_genesis_time();
        let genesis_time = 1_549_935_547;

        info!(log,"Beacon node connected"; "Node Version" => node_info.version.clone(), "Chain ID" => node_info.chain_id, "Genesis time" => genesis_time);

        let proto_fork = node_info.get_fork();
        let mut previous_version: [u8; 4] = [0; 4];
        let mut current_version: [u8; 4] = [0; 4];
        previous_version.copy_from_slice(&proto_fork.get_previous_version()[..4]);
        current_version.copy_from_slice(&proto_fork.get_current_version()[..4]);
        let fork = Fork {
            previous_version,
            current_version,
            epoch: Epoch::from(proto_fork.get_epoch()),
        };

        // build the validator slot clock
        let slot_clock = {
            let clock = SystemTimeSlotClock::new(genesis_time, config.spec.seconds_per_slot)
                .expect("Unable to instantiate SystemTimeSlotClock.");
            Arc::new(clock)
        };

        // initialize the RPC clients

        // Beacon node gRPC beacon block endpoints.
        let beacon_block_client = {
            let ch = ChannelBuilder::new(env.clone()).connect(&config.server);
            Arc::new(BeaconBlockServiceClient::new(ch))
        };

        // Beacon node gRPC validator endpoints.
        let validator_client = {
            let ch = ChannelBuilder::new(env.clone()).connect(&config.server);
            Arc::new(ValidatorServiceClient::new(ch))
        };

        //Beacon node gRPC attester endpoints.
        let attester_client = {
            let ch = ChannelBuilder::new(env.clone()).connect(&config.server);
            Arc::new(AttestationServiceClient::new(ch))
        };

        //TODO: Add error chain. Handle errors
        let current_slot = slot_clock.present_slot().unwrap().unwrap().sub(1);

        // calculate seconds to the next slot
        let micros_to_next_slot = {
            let syslot_time = SystemTime::now();
            let duration_since_epoch = syslot_time.duration_since(SystemTime::UNIX_EPOCH).unwrap();
            debug!(log, "Duration since unix epoch {:?}", duration_since_epoch);
            let mut micros_to_slot = None;
            if let Some(duration_since_genesis) =
                duration_since_epoch.checked_sub(Duration::from_secs(genesis_time))
            {
                // seconds till next slot
                debug!(log, "Genesis Time {:?}", genesis_time);
                debug!(log, "Duration since genesis {:?}", duration_since_genesis);
                micros_to_slot = duration_since_genesis
                    .as_secs()
                    .checked_rem(config.spec.seconds_per_slot);
            }
            micros_to_slot.unwrap_or_else(|| 0)
            /*
            let duration_to_slot = duration_since_genesis
                .checked_sub(Duration::from(
                    duration_since_genesis
                        .checked_div(config.spec.seconds_per_slot as u64)
                        .unwrap()
                        .as_secs()
                        .checked_mul(config.spec.seconds_per_slot)
                        .unwrap(),
                ))
                .unwrap();
                */
        };

        info!(log, ""; "Micro Seconds to next slot"=>micros_to_next_slot);

        Self {
            connected_node_version: node_info.version,
            chain_id: node_info.chain_id as u16,
            fork,
            slot_clock,
            current_slot,
            micros_to_next_slot: Duration::from_micros(micros_to_next_slot),
            beacon_block_client,
            validator_client,
            attester_client,
            log,
        }
    }

    /// Initialise the service then run the core thread.
    pub fn start(config: ValidatorConfig, log: slog::Logger) {
        // connect to the node and retrieve its properties and initialize the gRPC clients
        let service = Service::initialize_service(&config, log);

        // we have connected to a node and established its parameters. Spin up the core service

        // set up the validator service runtime
        let mut runtime = Builder::new()
            .clock(Clock::system())
            .name_prefix("validator-client-")
            .build()
            .unwrap();

        // set up the validator work interval - start at next slot and proceed every slot
        // TODO: Error chain handle errors.
        let interval = {
            // Set the interval to start at the next slot, and every slot after
            let slot_duration = Duration::from_secs(config.spec.seconds_per_slot);
            //TODO: Handle checked add correctly
            Interval::new(Instant::now() + service.micros_to_next_slot, slot_duration)
        };

        // kick off core service

        // generate keypairs

        // TODO: keypairs are randomly generated; they should be loaded from a file or generated.
        // https://github.com/sigp/lighthouse/issues/160
        let keypairs = Arc::new(vec![Keypair::random()]);

        // build requisite objects to pass to core thread.
        let duties_map = Arc::new(EpochDutiesMap::new(config.spec.slots_per_epoch));
        let epoch_map_for_attester = Arc::new(EpochMap::new(config.spec.slots_per_epoch));
        let manager = DutiesManager {
            duties_map,
            pubkeys: keypairs.iter().map(|keypair| keypair.pk.clone()).collect(),
            spec: Arc::new(config.spec),
            slot_clock: service.slot_clock.clone(),
            beacon_node: service.validator_client.clone(),
        };

        runtime.block_on(interval.for_each(move |_| {
            // update duties
            debug!(
                service.log,
                "Processing slot: {}",
                service.slot_clock.present_slot().unwrap().unwrap().as_u64()
            );
            manager.poll();
            Ok(())
        }));
    }

    /*

    let duties_map = Arc::new(EpochDutiesMap::new(spec.slots_per_epoch));
    let epoch_map_for_attester = Arc::new(EpochMap::new(spec.slots_per_epoch));

    for keypair in keypairs {
        info!(self.log, "Starting validator services"; "validator" => keypair.pk.concatenated_hex_id());

        // Spawn a new thread to maintain the validator's `EpochDuties`.
        let duties_manager_thread = {
            let spec = spec.clone();
            let duties_map = duties_map.clone();
            let slot_clock = self.slot_clock.clone();
            let log = self.log.clone();
            let beacon_node = self.validator_client.clone();
            let pubkey = keypair.pk.clone();
            thread::spawn(move || {
                let manager = DutiesManager {
                    duties_map,
                    pubkey,
                    spec,
                    slot_clock,
                    beacon_node,
                };
                let mut duties_manager_service = DutiesManagerService {
                    manager,
                    poll_interval_millis,
                    log,
                };

                duties_manager_service.run();
            })
        };

        // Spawn a new thread to perform block production for the validator.
        let producer_thread = {
            let spec = spec.clone();
            let signer = Arc::new(BlockProposerLocalSigner::new(keypair.clone()));
            let duties_map = duties_map.clone();
            let slot_clock = slot_clock.clone();
            let log = log.clone();
            let client = Arc::new(BeaconBlockGrpcClient::new(beacon_block_grpc_client.clone()));
            thread::spawn(move || {
                let block_producer =
                    BlockProducer::new(spec, duties_map, slot_clock, client, signer);
                let mut block_producer_service = BlockProducerService {
                    block_producer,
                    poll_interval_millis,
                    log,
                };

                block_producer_service.run();
            })
        };

        // Spawn a new thread for attestation for the validator.
        let attester_thread = {
            let signer = Arc::new(AttesterLocalSigner::new(keypair.clone()));
            let epoch_map = epoch_map_for_attester.clone();
            let slot_clock = slot_clock.clone();
            let log = log.clone();
            let client = Arc::new(AttestationGrpcClient::new(attester_grpc_client.clone()));
            thread::spawn(move || {
                let attester = Attester::new(epoch_map, slot_clock, client, signer);
                let mut attester_service = AttesterService {
                    attester,
                    poll_interval_millis,
                    log,
                };

                attester_service.run();
            })
        };

        threads.push((duties_manager_thread, producer_thread, attester_thread));
    }

    // Naively wait for all the threads to complete.
    for tuple in threads {
        let (manager, producer, attester) = tuple;
        let _ = producer.join();
        let _ = manager.join();
        let _ = attester.join();
    }
    */
}
<commit_msg>Correct slot duration interval timer<commit_after>/// The validator service. Connects to a beacon node and signs blocks when required.
use crate::attester_service::{AttestationGrpcClient, AttesterService};
use crate::block_producer_service::{BeaconBlockGrpcClient, BlockProducerService};
use crate::config::Config as ValidatorConfig;
use crate::duties::{DutiesManager, DutiesManagerService, EpochDutiesMap};
use attester::test_utils::EpochMap;
use attester::{test_utils::LocalSigner as AttesterLocalSigner, Attester};
use block_proposer::{test_utils::LocalSigner as BlockProposerLocalSigner, BlockProducer};
use bls::Keypair;
use grpcio::{ChannelBuilder, EnvBuilder};
use protos::services::Empty;
use protos::services_grpc::{
    AttestationServiceClient, BeaconBlockServiceClient, BeaconNodeServiceClient,
    ValidatorServiceClient,
};
use slog::{debug, info, warn};
use slot_clock::{SlotClock, SystemTimeSlotClock};
use std::ops::Sub;
use std::sync::Arc;
use std::time::{Duration, Instant, SystemTime};
use tokio::prelude::*;
use tokio::runtime::Builder;
use tokio::timer::Interval;
use tokio_timer::clock::Clock;
use types::{Epoch, Fork, Slot};

//TODO: This service should be simplified in the future. Can be made more steamlined.

/// The validator service. This is the main thread that executes and maintains validator
/// duties.
pub struct Service {
    /// The node we currently connected to.
    connected_node_version: String,
    /// The chain id we are processing on.
    chain_id: u16,
    /// The fork state we processing on.
    fork: Fork,
    /// The slot clock keeping track of time.
    slot_clock: Arc<SystemTimeSlotClock>,
    /// The current slot we are processing.
    current_slot: Slot,
    /// Duration until the next slot. This is used for initializing the tokio timer interval.
    duration_to_next_slot: Duration,
    // GRPC Clients
    /// The beacon block GRPC client.
    beacon_block_client: Arc<BeaconBlockServiceClient>,
    /// The validator GRPC client.
    validator_client: Arc<ValidatorServiceClient>,
    /// The attester GRPC client.
    attester_client: Arc<AttestationServiceClient>,
    /// The validator client logger.
    log: slog::Logger,
}

impl Service {
    ///  Initial connection to the beacon node to determine its properties.
    ///
    ///  This tries to connect to a beacon node. Once connected, it initialised the gRPC clients
    ///  and returns an instance of the service.
    fn initialize_service(config: &ValidatorConfig, log: slog::Logger) -> Self {
        // initialise the beacon node client to check for a connection

        let env = Arc::new(EnvBuilder::new().build());
        // Beacon node gRPC beacon node endpoints.
        let beacon_node_client = {
            let ch = ChannelBuilder::new(env.clone()).connect(&config.server);
            Arc::new(BeaconNodeServiceClient::new(ch))
        };

        // retrieve node information
        let node_info = loop {
            let info = match beacon_node_client.info(&Empty::new()) {
                Err(e) => {
                    warn!(log, "Could not connect to node. Error: {}", e);
                    info!(log, "Retrying in 5 seconds...");
                    std::thread::sleep(Duration::from_secs(5));
                    continue;
                }
                Ok(info) => {
                    if SystemTime::now()
                        .duration_since(SystemTime::UNIX_EPOCH)
                        .unwrap()
                        > Duration::from_secs(info.genesis_time)
                    {
                        warn!(
                            log,
                            "Beacon Node's genesis time is in the future. No work to do.\n Exiting"
                        );
                        //                        return Err("Genesis Time in the future");
                    }
                    break info;
                }
            };
        };

        // build requisite objects to form Self
        let genesis_time = node_info.get_genesis_time();

        info!(log,"Beacon node connected"; "Node Version" => node_info.version.clone(), "Chain ID" => node_info.chain_id, "Genesis time" => genesis_time);

        let proto_fork = node_info.get_fork();
        let mut previous_version: [u8; 4] = [0; 4];
        let mut current_version: [u8; 4] = [0; 4];
        previous_version.copy_from_slice(&proto_fork.get_previous_version()[..4]);
        current_version.copy_from_slice(&proto_fork.get_current_version()[..4]);
        let fork = Fork {
            previous_version,
            current_version,
            epoch: Epoch::from(proto_fork.get_epoch()),
        };

        // build the validator slot clock
        let slot_clock = {
            let clock = SystemTimeSlotClock::new(genesis_time, config.spec.seconds_per_slot)
                .expect("Unable to instantiate SystemTimeSlotClock.");
            Arc::new(clock)
        };

        // initialize the RPC clients

        // Beacon node gRPC beacon block endpoints.
        let beacon_block_client = {
            let ch = ChannelBuilder::new(env.clone()).connect(&config.server);
            Arc::new(BeaconBlockServiceClient::new(ch))
        };

        // Beacon node gRPC validator endpoints.
        let validator_client = {
            let ch = ChannelBuilder::new(env.clone()).connect(&config.server);
            Arc::new(ValidatorServiceClient::new(ch))
        };

        //Beacon node gRPC attester endpoints.
        let attester_client = {
            let ch = ChannelBuilder::new(env.clone()).connect(&config.server);
            Arc::new(AttestationServiceClient::new(ch))
        };

        //TODO: Add error chain. Handle errors
        let current_slot = slot_clock.present_slot().unwrap().unwrap().sub(1);

        // calculate the duration to the next slot
        let duration_to_next_slot = {
            let syslot_time = SystemTime::now();
            let duration_since_epoch = syslot_time.duration_since(SystemTime::UNIX_EPOCH).unwrap();
            let mut duration_to_next_slot = None;
            if let Some(duration_since_genesis) =
                duration_since_epoch.checked_sub(Duration::from_secs(genesis_time))
            {
                let elapsed_slots = duration_since_epoch
                    .as_secs()
                    .checked_div(config.spec.seconds_per_slot as u64)
                    .unwrap();
                duration_to_next_slot = Some(
                    Duration::from_secs(
                        (elapsed_slots + 1)
                            .checked_mul(config.spec.seconds_per_slot)
                            .unwrap(),
                    )
                    .checked_sub(duration_since_genesis)
                    .expect("This should never saturate"),
                );
            }
            duration_to_next_slot.unwrap_or_else(|| Duration::from_secs(0))
        };

        Self {
            connected_node_version: node_info.version,
            chain_id: node_info.chain_id as u16,
            fork,
            slot_clock,
            current_slot,
            duration_to_next_slot,
            beacon_block_client,
            validator_client,
            attester_client,
            log,
        }
    }

    /// Initialise the service then run the core thread.
    pub fn start(config: ValidatorConfig, log: slog::Logger) {
        // connect to the node and retrieve its properties and initialize the gRPC clients
        let service = Service::initialize_service(&config, log);

        // we have connected to a node and established its parameters. Spin up the core service

        // set up the validator service runtime
        let mut runtime = Builder::new()
            .clock(Clock::system())
            .name_prefix("validator-client-")
            .build()
            .unwrap();

        // set up the validator work interval - start at next slot and proceed every slot
        // TODO: Error chain handle errors.
        let interval = {
            // Set the interval to start at the next slot, and every slot after
            let slot_duration = Duration::from_secs(config.spec.seconds_per_slot);
            //TODO: Handle checked add correctly
            Interval::new(
                Instant::now() + service.duration_to_next_slot,
                slot_duration,
            )
        };

        // kick off core service

        // generate keypairs

        // TODO: keypairs are randomly generated; they should be loaded from a file or generated.
        // https://github.com/sigp/lighthouse/issues/160
        let keypairs = Arc::new(vec![Keypair::random()]);

        // build requisite objects to pass to core thread.
        let duties_map = Arc::new(EpochDutiesMap::new(config.spec.slots_per_epoch));
        let epoch_map_for_attester = Arc::new(EpochMap::new(config.spec.slots_per_epoch));
        let manager = DutiesManager {
            duties_map,
            pubkeys: keypairs.iter().map(|keypair| keypair.pk.clone()).collect(),
            spec: Arc::new(config.spec),
            slot_clock: service.slot_clock.clone(),
            beacon_node: service.validator_client.clone(),
        };

        runtime.block_on(interval.for_each(move |_| {
            // update duties
            debug!(
                service.log,
                "Processing slot: {}",
                service.slot_clock.present_slot().unwrap().unwrap().as_u64()
            );
            manager.poll();
            Ok(())
        }));
    }

    /*

    let duties_map = Arc::new(EpochDutiesMap::new(spec.slots_per_epoch));
    let epoch_map_for_attester = Arc::new(EpochMap::new(spec.slots_per_epoch));

    for keypair in keypairs {
        info!(self.log, "Starting validator services"; "validator" => keypair.pk.concatenated_hex_id());

        // Spawn a new thread to maintain the validator's `EpochDuties`.
        let duties_manager_thread = {
            let spec = spec.clone();
            let duties_map = duties_map.clone();
            let slot_clock = self.slot_clock.clone();
            let log = self.log.clone();
            let beacon_node = self.validator_client.clone();
            let pubkey = keypair.pk.clone();
            thread::spawn(move || {
                let manager = DutiesManager {
                    duties_map,
                    pubkey,
                    spec,
                    slot_clock,
                    beacon_node,
                };
                let mut duties_manager_service = DutiesManagerService {
                    manager,
                    poll_interval_millis,
                    log,
                };

                duties_manager_service.run();
            })
        };

        // Spawn a new thread to perform block production for the validator.
        let producer_thread = {
            let spec = spec.clone();
            let signer = Arc::new(BlockProposerLocalSigner::new(keypair.clone()));
            let duties_map = duties_map.clone();
            let slot_clock = slot_clock.clone();
            let log = log.clone();
            let client = Arc::new(BeaconBlockGrpcClient::new(beacon_block_grpc_client.clone()));
            thread::spawn(move || {
                let block_producer =
                    BlockProducer::new(spec, duties_map, slot_clock, client, signer);
                let mut block_producer_service = BlockProducerService {
                    block_producer,
                    poll_interval_millis,
                    log,
                };

                block_producer_service.run();
            })
        };

        // Spawn a new thread for attestation for the validator.
        let attester_thread = {
            let signer = Arc::new(AttesterLocalSigner::new(keypair.clone()));
            let epoch_map = epoch_map_for_attester.clone();
            let slot_clock = slot_clock.clone();
            let log = log.clone();
            let client = Arc::new(AttestationGrpcClient::new(attester_grpc_client.clone()));
            thread::spawn(move || {
                let attester = Attester::new(epoch_map, slot_clock, client, signer);
                let mut attester_service = AttesterService {
                    attester,
                    poll_interval_millis,
                    log,
                };

                attester_service.run();
            })
        };

        threads.push((duties_manager_thread, producer_thread, attester_thread));
    }

    // Naively wait for all the threads to complete.
    for tuple in threads {
        let (manager, producer, attester) = tuple;
        let _ = producer.join();
        let _ = manager.join();
        let _ = attester.join();
    }
    */
}
<|endoftext|>


<commit_before>use ::quote::Tokens;
use ::syn::Ident;
use ::yaml_rust::Yaml;

use infer::datatype;
use infer::path;
use infer::TokensResult;

type FieldsAndSupportingTypes = (Vec<Tokens>, Vec<Tokens>);

fn infer_ref(schema: &Yaml) -> FieldsAndSupportingTypes {
    let mut fields: Vec<Tokens> = Vec::new();
    let mut additional_types: Vec<Tokens> = Vec::new();

    for (field_name, schema) in schema["properties"].as_hash()
        .expect("Properties must be a map.")
    {
        let field_name = field_name.as_str()
            .expect("Property must be a string.");
        let field_ident = Ident::new(field_name);
        let (field_type, additional_type) = datatype::infer_v3(&schema).unwrap();

        fields.push(quote!{ #field_ident: #field_type });
        if let Some(additional_type) = additional_type {
            additional_types.push(additional_type);
        }
    }

    (fields, additional_types)
}

pub(super) fn infer_v3(schema: &Yaml) -> TokensResult {
    let paths = schema["paths"].clone();
    let path_impls: Vec<Tokens> = paths.as_hash()
        .expect("Paths must be a map.")
        .iter()
        .map(|(path, path_schema)| path::infer_v3(
            path.as_str().expect("Path must be a string."), &path_schema
        ).unwrap())
        .collect();

    let api_url = schema["servers"][0]["url"].as_str()
        .expect("Must have at least one server URL.");

    let mut schema_ref_struct_defs: Vec<Tokens> = Vec::new();
    for (schema_ref, schema) in schema["components"]["schemas"].as_hash()
        .expect("#/components/schemas must be a map.")
    {
        let schema_ref_name = schema_ref.as_str()
            .expect("$ref name must be a string.");
        let ident = Ident::new(schema_ref_name);
        let (fields, additional_types) = infer_ref(schema);

        schema_ref_struct_defs.push(quote! {
            #(#additional_types)*

            #[derive(Deserialize)]
            pub struct #ident {
                #(#fields),*
            }
        });
    }

    Ok(quote! {
        #[macro_use]
        extern crate serde_derive;

        use tapioca::serde as serde;
        use tapioca::serde::json as serde_json;

        mod schema_ref {
            #(#schema_ref_struct_defs)*
        }

        const API_URL: &'static str = #api_url;

        #(#path_impls)*
    })
}
<commit_msg>Fix namespacing for nested $ref types<commit_after>use ::quote::Tokens;
use ::syn::Ident;
use ::yaml_rust::Yaml;

use infer::datatype;
use infer::path;
use infer::TokensResult;

type FieldsAndSupportingTypes = (Vec<Tokens>, Vec<Tokens>);

fn infer_ref(schema: &Yaml) -> FieldsAndSupportingTypes {
    let mut fields: Vec<Tokens> = Vec::new();
    let mut additional_types: Vec<Tokens> = Vec::new();

    for (field_name, schema) in schema["properties"].as_hash()
        .expect("Properties must be a map.")
    {
        let field_name = field_name.as_str()
            .expect("Property must be a string.");
        let field_ident = Ident::new(field_name);
        let (field_type, additional_type) = datatype::infer_v3(&schema).unwrap();

        fields.push(quote!{ #field_ident: #field_type });
        if let Some(additional_type) = additional_type {
            additional_types.push(additional_type);
        }
    }

    (fields, additional_types)
}

pub(super) fn infer_v3(schema: &Yaml) -> TokensResult {
    let paths = schema["paths"].clone();
    let path_impls: Vec<Tokens> = paths.as_hash()
        .expect("Paths must be a map.")
        .iter()
        .map(|(path, path_schema)| path::infer_v3(
            path.as_str().expect("Path must be a string."), &path_schema
        ).unwrap())
        .collect();

    let api_url = schema["servers"][0]["url"].as_str()
        .expect("Must have at least one server URL.");

    let mut schema_ref_struct_defs: Vec<Tokens> = Vec::new();
    for (schema_ref, schema) in schema["components"]["schemas"].as_hash()
        .expect("#/components/schemas must be a map.")
    {
        let schema_ref_name = schema_ref.as_str()
            .expect("$ref name must be a string.");
        let ident = Ident::new(schema_ref_name);
        let (fields, additional_types) = infer_ref(schema);

        schema_ref_struct_defs.push(quote! {
            #(#additional_types)*

            #[derive(Deserialize)]
            pub struct #ident {
                #(#fields),*
            }
        });
    }

    Ok(quote! {
        #[macro_use]
        extern crate serde_derive;

        use tapioca::serde as serde;
        use tapioca::serde::json as serde_json;

        mod schema_ref {
            use super::schema_ref;

            #(#schema_ref_struct_defs)*
        }

        const API_URL: &'static str = #api_url;

        #(#path_impls)*
    })
}
<|endoftext|>


<commit_before>extern crate serde;

use std::fmt;
use std::ops::{Deref, DerefMut};

use {Error, Result};
use api::Pushable;
use thread::{Context, Thread, ThreadInternal};
use types::{VmTag, VmIndex};
use value::{Def, Value};
use self::serde::ser::{self, Serialize};

pub fn to_value<T>(thread: &Thread, value: &T) -> Result<Value>
    where T: Serialize
{
    let mut context = thread.context();
    let mut serializer = Serializer {
        thread: thread,
        context: &mut context,
    };
    value.serialize(&mut serializer)?;
    Ok(serializer.context.stack.pop())
}

impl ser::Error for Error {
    fn custom<T>(msg: T) -> Self
        where T: fmt::Display
    {
        Error::Message(format!("{}", msg))
    }
}

struct Serializer<'t> {
    thread: &'t Thread,
    context: &'t mut Context,
}

impl<'t> Serializer<'t> {
    fn to_value<T>(&mut self, value: T) -> Result<()>
        where T: Pushable<'t>
    {
        value.push(self.thread, self.context)
    }

    fn alloc(&mut self, tag: VmTag, values: VmIndex) -> Result<()> {
        let value = self.context
            .gc
            .alloc(Def {
                       tag: tag,
                       elems: &self.context.stack[self.context.stack.len() - values..],
                   })?;
        for _ in 0..values {
            self.context.stack.pop();
        }
        self.context.stack.push(Value::Data(value));
        Ok(())
    }
}

struct RecordSerializer<'s, 'vm: 's> {
    serializer: &'s mut Serializer<'vm>,
    variant_index: VmTag,
    values: VmIndex,
}

impl<'s, 'vm> Deref for RecordSerializer<'s, 'vm> {
    type Target = Serializer<'vm>;
    fn deref(&self) -> &Self::Target {
        self.serializer
    }
}

impl<'s, 'vm> DerefMut for RecordSerializer<'s, 'vm> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        self.serializer
    }
}

impl<'s, 'vm> RecordSerializer<'s, 'vm> {
    fn new(serializer: &'s mut Serializer<'vm>, variant_index: u32) -> Self {
        RecordSerializer {
            serializer: serializer,
            variant_index: variant_index,
            values: 0,
        }
    }
}

impl<'a, 'vm> ser::Serializer for &'a mut Serializer<'vm> {
    type Ok = ();

    // The error type when some error occurs during serialization.
    type Error = Error;

    // Associated types for keeping track of additional state while serializing
    // compound data structures like sequences and maps. In this case no
    // additional state is required beyond what is already stored in the
    // Serializer struct.
    type SerializeSeq = RecordSerializer<'a, 'vm>;
    type SerializeTuple = RecordSerializer<'a, 'vm>;
    type SerializeTupleStruct = RecordSerializer<'a, 'vm>;
    type SerializeTupleVariant = RecordSerializer<'a, 'vm>;
    type SerializeMap = RecordSerializer<'a, 'vm>;
    type SerializeStruct = RecordSerializer<'a, 'vm>;
    type SerializeStructVariant = RecordSerializer<'a, 'vm>;

    // Here we go with the simple methods. The following 12 methods receive one
    // of the primitive types of the data model and map it to JSON by appending
    // into the output string.
    fn serialize_bool(self, v: bool) -> Result<Self::Ok> {
        self.to_value(v)
    }

    // JSON does not distinguish between different sizes of integers, so all
    // signed integers will be serialized the same and all unsigned integers
    // will be serialized the same. Other formats, especially compact binary
    // formats, may need independent logic for the different sizes.
    fn serialize_i8(self, v: i8) -> Result<Self::Ok> {
        self.serialize_i64(v as i64)
    }

    fn serialize_i16(self, v: i16) -> Result<Self::Ok> {
        self.serialize_i64(v as i64)
    }

    fn serialize_i32(self, v: i32) -> Result<Self::Ok> {
        self.serialize_i64(v as i64)
    }

    // Not particularly efficient but this is example code anyway. A more
    // performant approach would be to use the `itoa` crate.
    fn serialize_i64(self, v: i64) -> Result<Self::Ok> {
        self.to_value(v as isize)
    }

    fn serialize_u8(self, v: u8) -> Result<Self::Ok> {
        self.serialize_u64(v as u64)
    }

    fn serialize_u16(self, v: u16) -> Result<Self::Ok> {
        self.serialize_u64(v as u64)
    }

    fn serialize_u32(self, v: u32) -> Result<Self::Ok> {
        self.serialize_u64(v as u64)
    }

    fn serialize_u64(self, v: u64) -> Result<Self::Ok> {
        self.to_value(v as isize)
    }

    fn serialize_f32(self, v: f32) -> Result<Self::Ok> {
        self.serialize_f64(v as f64)
    }

    fn serialize_f64(self, v: f64) -> Result<Self::Ok> {
        self.to_value(v)
    }

    fn serialize_char(self, v: char) -> Result<Self::Ok> {
        self.serialize_str(&v.to_string())
    }

    fn serialize_str(self, v: &str) -> Result<Self::Ok> {
        self.to_value(v)
    }

    fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok> {
        self.to_value(v)
    }

    // An absent optional is represented as the JSON `null`.
    fn serialize_none(self) -> Result<Self::Ok> {
        self.serialize_unit()
    }

    // A present optional is represented as just the contained value. Note that
    // this is a lossy representation. For example the values `Some(())` and
    // `None` both serialize as just `null`. Unfortunately this is typically
    // what people expect when working with JSON. Other formats are encouraged
    // to behave more intelligently if possible.
    fn serialize_some<T>(self, value: &T) -> Result<Self::Ok>
        where T: ?Sized + Serialize
    {
        value.serialize(self)
    }

    fn serialize_unit(self) -> Result<Self::Ok> {
        self.context.stack.push(Value::Tag(0));
        Ok(())
    }

    fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok> {
        self.serialize_unit()
    }

    fn serialize_unit_variant(self,
                              _name: &'static str,
                              variant_index: u32,
                              _variant: &'static str)
                              -> Result<Self::Ok> {
        self.context.stack.push(Value::Tag(variant_index));
        Ok(())
    }

    // As is done here, serializers are encouraged to treat newtype structs as
    // insignificant wrappers around the data they contain.
    fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Self::Ok>
        where T: ?Sized + Serialize
    {
        value.serialize(self)
    }

    // Note that newtype variant (and all of the other variant serialization
    // methods) refer exclusively to the "externally tagged" enum
    // representation.
    //
    // Serialize this to JSON in externally tagged form as `{ NAME: VALUE }`.
    fn serialize_newtype_variant<T>(self,
                                    _name: &'static str,
                                    variant_index: u32,
                                    _variant: &'static str,
                                    value: &T)
                                    -> Result<Self::Ok>
        where T: ?Sized + Serialize
    {
        value.serialize(&mut *self)?;
        self.alloc(variant_index, 1)
    }

    fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
        Ok(RecordSerializer::new(self, 0))
    }

    fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> {
        self.serialize_seq(Some(len))
    }

    fn serialize_tuple_struct(self,
                              _name: &'static str,
                              len: usize)
                              -> Result<Self::SerializeTupleStruct> {
        self.serialize_seq(Some(len))
    }

    fn serialize_tuple_variant(self,
                               _name: &'static str,
                               variant_index: u32,
                               _variant: &'static str,
                               _len: usize)
                               -> Result<Self::SerializeTupleVariant> {
        Ok(RecordSerializer::new(self, variant_index))
    }

    fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
        Ok(RecordSerializer::new(self, 0))
    }

    fn serialize_struct(self, _name: &'static str, len: usize) -> Result<Self::SerializeStruct> {
        self.serialize_map(Some(len))
    }

    fn serialize_struct_variant(self,
                                _name: &'static str,
                                variant_index: u32,
                                _variant: &'static str,
                                _len: usize)
                                -> Result<Self::SerializeStructVariant> {
        Ok(RecordSerializer::new(self, variant_index))
    }
}

impl<'a, 'vm> ser::SerializeSeq for RecordSerializer<'a, 'vm> {
    type Ok = ();
    type Error = Error;

    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
        where T: ?Sized + Serialize
    {
        value.serialize(&mut **self)
    }

    fn end(self) -> Result<Self::Ok> {
        self.serializer.alloc(self.variant_index, self.values)
    }
}

impl<'a, 'vm> ser::SerializeTuple for RecordSerializer<'a, 'vm> {
    type Ok = ();
    type Error = Error;

    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
        where T: ?Sized + Serialize
    {
        value.serialize(&mut **self)
    }

    fn end(self) -> Result<Self::Ok> {
        self.serializer.alloc(self.variant_index, self.values)
    }
}

impl<'a, 'vm> ser::SerializeTupleStruct for RecordSerializer<'a, 'vm> {
    type Ok = ();
    type Error = Error;

    fn serialize_field<T>(&mut self, value: &T) -> Result<()>
        where T: ?Sized + Serialize
    {
        value.serialize(&mut **self)
    }

    fn end(self) -> Result<Self::Ok> {
        self.serializer.alloc(self.variant_index, self.values)
    }
}

impl<'a, 'vm> ser::SerializeTupleVariant for RecordSerializer<'a, 'vm> {
    type Ok = ();
    type Error = Error;

    fn serialize_field<T>(&mut self, value: &T) -> Result<()>
        where T: ?Sized + Serialize
    {
        value.serialize(&mut **self)
    }

    fn end(self) -> Result<Self::Ok> {
        self.serializer.alloc(self.variant_index, self.values)
    }
}

impl<'a, 'vm> ser::SerializeMap for RecordSerializer<'a, 'vm> {
    type Ok = ();
    type Error = Error;

    fn serialize_key<T>(&mut self, _key: &T) -> Result<()>
        where T: ?Sized + Serialize
    {
        Ok(())
    }

    fn serialize_value<T>(&mut self, value: &T) -> Result<()>
        where T: ?Sized + Serialize
    {
        value.serialize(&mut **self)
    }

    fn end(self) -> Result<Self::Ok> {
        self.serializer.alloc(self.variant_index, self.values)
    }
}

impl<'a, 'vm> ser::SerializeStruct for RecordSerializer<'a, 'vm> {
    type Ok = ();
    type Error = Error;

    fn serialize_field<T>(&mut self, _key: &'static str, value: &T) -> Result<()>
        where T: ?Sized + Serialize
    {
        value.serialize(&mut **self)
    }

    fn end(self) -> Result<Self::Ok> {
        self.serializer.alloc(self.variant_index, self.values)
    }
}

// Similar to `SerializeTupleVariant`, here the `end` method is responsible for
// closing both of the curly braces opened by `serialize_struct_variant`.
impl<'a, 'vm> ser::SerializeStructVariant for RecordSerializer<'a, 'vm> {
    type Ok = ();
    type Error = Error;

    fn serialize_field<T>(&mut self, _key: &'static str, value: &T) -> Result<()>
        where T: ?Sized + Serialize
    {
        value.serialize(&mut **self)?;
        self.values += 1;
        Ok(())
    }

    fn end(self) -> Result<Self::Ok> {
        self.serializer.alloc(self.variant_index, self.values)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use thread::{RootedThread, ThreadInternal};

    #[derive(Serialize)]
    struct Test {
        test: i32,
        string: String,
    }

    fn make_value<'vm, T>(thread: &'vm Thread, value: T) -> Value
        where T: Pushable<'vm>
    {
        let mut context = thread.context();
        value.push(thread, &mut context);
        context.stack.pop()
    }

    #[test]
    fn bool() {
        let thread = RootedThread::new();
        assert_eq!(to_value(&thread, &true).unwrap(), Value::Tag(1));
    }
}
<commit_msg>test: Add a test for enum serializing<commit_after>extern crate serde;

use std::fmt;
use std::ops::{Deref, DerefMut};

use {Error, Result};
use api::Pushable;
use thread::{Context, Thread, ThreadInternal};
use types::{VmTag, VmIndex};
use value::{Def, Value};
use self::serde::ser::{self, Serialize};

pub fn to_value<T>(thread: &Thread, value: &T) -> Result<Value>
    where T: Serialize
{
    let mut context = thread.context();
    let mut serializer = Serializer {
        thread: thread,
        context: &mut context,
    };
    value.serialize(&mut serializer)?;
    Ok(serializer.context.stack.pop())
}

impl ser::Error for Error {
    fn custom<T>(msg: T) -> Self
        where T: fmt::Display
    {
        Error::Message(format!("{}", msg))
    }
}

struct Serializer<'t> {
    thread: &'t Thread,
    context: &'t mut Context,
}

impl<'t> Serializer<'t> {
    fn to_value<T>(&mut self, value: T) -> Result<()>
        where T: Pushable<'t>
    {
        value.push(self.thread, self.context)
    }

    fn alloc(&mut self, tag: VmTag, values: VmIndex) -> Result<()> {
        let value = self.context
            .gc
            .alloc(Def {
                       tag: tag,
                       elems: &self.context.stack[self.context.stack.len() - values..],
                   })?;
        for _ in 0..values {
            self.context.stack.pop();
        }
        self.context.stack.push(Value::Data(value));
        Ok(())
    }
}

struct RecordSerializer<'s, 'vm: 's> {
    serializer: &'s mut Serializer<'vm>,
    variant_index: VmTag,
    values: VmIndex,
}

impl<'s, 'vm> Deref for RecordSerializer<'s, 'vm> {
    type Target = Serializer<'vm>;
    fn deref(&self) -> &Self::Target {
        self.serializer
    }
}

impl<'s, 'vm> DerefMut for RecordSerializer<'s, 'vm> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        self.serializer
    }
}

impl<'s, 'vm> RecordSerializer<'s, 'vm> {
    fn new(serializer: &'s mut Serializer<'vm>, variant_index: u32) -> Self {
        RecordSerializer {
            serializer: serializer,
            variant_index: variant_index,
            values: 0,
        }
    }
}

impl<'a, 'vm> ser::Serializer for &'a mut Serializer<'vm> {
    type Ok = ();

    // The error type when some error occurs during serialization.
    type Error = Error;

    // Associated types for keeping track of additional state while serializing
    // compound data structures like sequences and maps. In this case no
    // additional state is required beyond what is already stored in the
    // Serializer struct.
    type SerializeSeq = RecordSerializer<'a, 'vm>;
    type SerializeTuple = RecordSerializer<'a, 'vm>;
    type SerializeTupleStruct = RecordSerializer<'a, 'vm>;
    type SerializeTupleVariant = RecordSerializer<'a, 'vm>;
    type SerializeMap = RecordSerializer<'a, 'vm>;
    type SerializeStruct = RecordSerializer<'a, 'vm>;
    type SerializeStructVariant = RecordSerializer<'a, 'vm>;

    // Here we go with the simple methods. The following 12 methods receive one
    // of the primitive types of the data model and map it to JSON by appending
    // into the output string.
    fn serialize_bool(self, v: bool) -> Result<Self::Ok> {
        self.to_value(v)
    }

    // JSON does not distinguish between different sizes of integers, so all
    // signed integers will be serialized the same and all unsigned integers
    // will be serialized the same. Other formats, especially compact binary
    // formats, may need independent logic for the different sizes.
    fn serialize_i8(self, v: i8) -> Result<Self::Ok> {
        self.serialize_i64(v as i64)
    }

    fn serialize_i16(self, v: i16) -> Result<Self::Ok> {
        self.serialize_i64(v as i64)
    }

    fn serialize_i32(self, v: i32) -> Result<Self::Ok> {
        self.serialize_i64(v as i64)
    }

    // Not particularly efficient but this is example code anyway. A more
    // performant approach would be to use the `itoa` crate.
    fn serialize_i64(self, v: i64) -> Result<Self::Ok> {
        self.to_value(v as isize)
    }

    fn serialize_u8(self, v: u8) -> Result<Self::Ok> {
        self.serialize_u64(v as u64)
    }

    fn serialize_u16(self, v: u16) -> Result<Self::Ok> {
        self.serialize_u64(v as u64)
    }

    fn serialize_u32(self, v: u32) -> Result<Self::Ok> {
        self.serialize_u64(v as u64)
    }

    fn serialize_u64(self, v: u64) -> Result<Self::Ok> {
        self.to_value(v as isize)
    }

    fn serialize_f32(self, v: f32) -> Result<Self::Ok> {
        self.serialize_f64(v as f64)
    }

    fn serialize_f64(self, v: f64) -> Result<Self::Ok> {
        self.to_value(v)
    }

    fn serialize_char(self, v: char) -> Result<Self::Ok> {
        self.serialize_str(&v.to_string())
    }

    fn serialize_str(self, v: &str) -> Result<Self::Ok> {
        self.to_value(v)
    }

    fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok> {
        self.to_value(v)
    }

    // An absent optional is represented as the JSON `null`.
    fn serialize_none(self) -> Result<Self::Ok> {
        self.serialize_unit()
    }

    // A present optional is represented as just the contained value. Note that
    // this is a lossy representation. For example the values `Some(())` and
    // `None` both serialize as just `null`. Unfortunately this is typically
    // what people expect when working with JSON. Other formats are encouraged
    // to behave more intelligently if possible.
    fn serialize_some<T>(self, value: &T) -> Result<Self::Ok>
        where T: ?Sized + Serialize
    {
        value.serialize(self)
    }

    fn serialize_unit(self) -> Result<Self::Ok> {
        self.context.stack.push(Value::Tag(0));
        Ok(())
    }

    fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok> {
        self.serialize_unit()
    }

    fn serialize_unit_variant(self,
                              _name: &'static str,
                              variant_index: u32,
                              _variant: &'static str)
                              -> Result<Self::Ok> {
        self.context.stack.push(Value::Tag(variant_index));
        Ok(())
    }

    // As is done here, serializers are encouraged to treat newtype structs as
    // insignificant wrappers around the data they contain.
    fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Self::Ok>
        where T: ?Sized + Serialize
    {
        value.serialize(self)
    }

    // Note that newtype variant (and all of the other variant serialization
    // methods) refer exclusively to the "externally tagged" enum
    // representation.
    //
    // Serialize this to JSON in externally tagged form as `{ NAME: VALUE }`.
    fn serialize_newtype_variant<T>(self,
                                    _name: &'static str,
                                    variant_index: u32,
                                    _variant: &'static str,
                                    value: &T)
                                    -> Result<Self::Ok>
        where T: ?Sized + Serialize
    {
        value.serialize(&mut *self)?;
        self.alloc(variant_index, 1)
    }

    fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
        Ok(RecordSerializer::new(self, 0))
    }

    fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> {
        self.serialize_seq(Some(len))
    }

    fn serialize_tuple_struct(self,
                              _name: &'static str,
                              len: usize)
                              -> Result<Self::SerializeTupleStruct> {
        self.serialize_seq(Some(len))
    }

    fn serialize_tuple_variant(self,
                               _name: &'static str,
                               variant_index: u32,
                               _variant: &'static str,
                               _len: usize)
                               -> Result<Self::SerializeTupleVariant> {
        Ok(RecordSerializer::new(self, variant_index))
    }

    fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
        Ok(RecordSerializer::new(self, 0))
    }

    fn serialize_struct(self, _name: &'static str, len: usize) -> Result<Self::SerializeStruct> {
        self.serialize_map(Some(len))
    }

    fn serialize_struct_variant(self,
                                _name: &'static str,
                                variant_index: u32,
                                _variant: &'static str,
                                _len: usize)
                                -> Result<Self::SerializeStructVariant> {
        Ok(RecordSerializer::new(self, variant_index))
    }
}

impl<'a, 'vm> ser::SerializeSeq for RecordSerializer<'a, 'vm> {
    type Ok = ();
    type Error = Error;

    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
        where T: ?Sized + Serialize
    {
        value.serialize(&mut **self)?;
        self.values += 1;
        Ok(())
    }

    fn end(self) -> Result<Self::Ok> {
        self.serializer.alloc(self.variant_index, self.values)
    }
}

impl<'a, 'vm> ser::SerializeTuple for RecordSerializer<'a, 'vm> {
    type Ok = ();
    type Error = Error;

    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
        where T: ?Sized + Serialize
    {
        value.serialize(&mut **self)?;
        self.values += 1;
        Ok(())
    }

    fn end(self) -> Result<Self::Ok> {
        self.serializer.alloc(self.variant_index, self.values)
    }
}

impl<'a, 'vm> ser::SerializeTupleStruct for RecordSerializer<'a, 'vm> {
    type Ok = ();
    type Error = Error;

    fn serialize_field<T>(&mut self, value: &T) -> Result<()>
        where T: ?Sized + Serialize
    {
        value.serialize(&mut **self)?;
        self.values += 1;
        Ok(())
    }

    fn end(self) -> Result<Self::Ok> {
        self.serializer.alloc(self.variant_index, self.values)
    }
}

impl<'a, 'vm> ser::SerializeTupleVariant for RecordSerializer<'a, 'vm> {
    type Ok = ();
    type Error = Error;

    fn serialize_field<T>(&mut self, value: &T) -> Result<()>
        where T: ?Sized + Serialize
    {
        value.serialize(&mut **self)?;
        self.values += 1;
        Ok(())
    }

    fn end(self) -> Result<Self::Ok> {
        self.serializer.alloc(self.variant_index, self.values)
    }
}

impl<'a, 'vm> ser::SerializeMap for RecordSerializer<'a, 'vm> {
    type Ok = ();
    type Error = Error;

    fn serialize_key<T>(&mut self, _key: &T) -> Result<()>
        where T: ?Sized + Serialize
    {
        Ok(())
    }

    fn serialize_value<T>(&mut self, value: &T) -> Result<()>
        where T: ?Sized + Serialize
    {
        value.serialize(&mut **self)?;
        self.values += 1;
        Ok(())
    }

    fn end(self) -> Result<Self::Ok> {
        self.serializer.alloc(self.variant_index, self.values)
    }
}

impl<'a, 'vm> ser::SerializeStruct for RecordSerializer<'a, 'vm> {
    type Ok = ();
    type Error = Error;

    fn serialize_field<T>(&mut self, _key: &'static str, value: &T) -> Result<()>
        where T: ?Sized + Serialize
    {
        value.serialize(&mut **self)?;
        self.values += 1;
        Ok(())
    }

    fn end(self) -> Result<Self::Ok> {
        self.serializer.alloc(self.variant_index, self.values)
    }
}

// Similar to `SerializeTupleVariant`, here the `end` method is responsible for
// closing both of the curly braces opened by `serialize_struct_variant`.
impl<'a, 'vm> ser::SerializeStructVariant for RecordSerializer<'a, 'vm> {
    type Ok = ();
    type Error = Error;

    fn serialize_field<T>(&mut self, _key: &'static str, value: &T) -> Result<()>
        where T: ?Sized + Serialize
    {
        value.serialize(&mut **self)?;
        self.values += 1;
        Ok(())
    }

    fn end(self) -> Result<Self::Ok> {
        self.serializer.alloc(self.variant_index, self.values)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use thread::{RootedThread, ThreadInternal};

    fn make_value<'vm, T>(thread: &'vm Thread, value: T) -> Value
        where T: Pushable<'vm>
    {
        let mut context = thread.context();
        value.push(thread, &mut context).unwrap();
        context.stack.pop()
    }

    #[test]
    fn bool() {
        let thread = RootedThread::new();
        assert_eq!(to_value(&thread, &true).unwrap(), Value::Tag(1));
    }

    #[derive(Serialize)]
    struct Record {
        test: i32,
        string: String,
    }

    #[test]
    fn record() {
        let thread = RootedThread::new();
        let actual = to_value(&thread,
                              &Record {
                                   test: 123,
                                   string: "abc".to_string(),
                               })
                .unwrap();
        assert_eq!(actual,
                   make_value(&thread,
                              record! {
                       test => 123,
                       string => "abc"
                   }));
    }

    #[derive(Serialize)]
    enum Enum {
        A(String),
        B(i32),
    }

    #[test]
    fn enum_() {
        let thread = RootedThread::new();

        let actual = to_value(&thread, &Enum::A("abc".to_string())).unwrap();
        let s = make_value(&thread, "abc");
        assert_eq!(actual,
                   Value::Data(thread
                                   .context()
                                   .gc
                                   .alloc(Def {
                                              tag: 0,
                                              elems: &[s],
                                          })
                                   .unwrap()));

        let actual = to_value(&thread, &Enum::B(1232)).unwrap();
        assert_eq!(actual,
                   Value::Data(thread
                                   .context()
                                   .gc
                                   .alloc(Def {
                                              tag: 1,
                                              elems: &[Value::Int(1232)],
                                          })
                                   .unwrap()));
    }
}
<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

//! Support for compiling scripts and modules in tests.

use compiler::{
    compiler::{compile_module, compile_program as compiler_compile_program},
    parser::parse_program,
};
use stdlib::stdlib::*;
use types::{
    account_address::AccountAddress,
    transaction::{Program, TransactionArgument},
};
use vm::file_format::CompiledModule;

/// Compile the provided Move code into a blob which can be used as the code for a [`Program`].
///
/// The script is compiled with the default account address (`0x0`).
pub fn compile_script(code: &str) -> Vec<u8> {
    let address = AccountAddress::default();
    let parsed_program = parse_program(code).expect("program must parse");
    let deps = stdlib_deps(&address);
    let compiled_program =
        compiler_compile_program(&address, &parsed_program, &deps).expect("program must compile");

    let mut serialized_script = Vec::<u8>::new();
    compiled_program
        .script
        .serialize(&mut serialized_script)
        .expect("script must serialize");
    serialized_script
}

/// Compile the provided Move code and arguments into a `Program` using `address` as the
/// self address for any modules in `code`.
pub fn compile_program_with_address(
    address: &AccountAddress,
    code: &str,
    args: Vec<TransactionArgument>,
) -> Program {
    let deps = stdlib_deps(&AccountAddress::default());
    let parsed_program = parse_program(&code).expect("program must parse");
    let compiled_program =
        compiler_compile_program(address, &parsed_program, &deps).expect("program must compile");

    let mut serialized_script = Vec::<u8>::new();
    compiled_program
        .script
        .serialize(&mut serialized_script)
        .expect("script must serialize");
    let mut serialized_modules = vec![];
    for m in compiled_program.modules {
        let mut module = vec![];
        m.serialize(&mut module).expect("module must serialize");
        serialized_modules.push(module);
    }
    Program::new(serialized_script, serialized_modules, args)
}

/// Compile the provided Move code and arguments into a `Program`.
///
/// This supports both scripts and modules defined in the same Move code. The code is compiled with
/// the default account address (`0x0`).
pub fn compile_program(code: &str, args: Vec<TransactionArgument>) -> Program {
    let address = AccountAddress::default();
    compile_program_with_address(&address, code, args)
}

fn stdlib_deps(address: &AccountAddress) -> Vec<CompiledModule> {
    let coin_module = coin_module();
    let compiled_coin_module =
        compile_module(&address, &coin_module, &[]).expect("coin must compile");

    let hash_module = native_hash_module();
    let compiled_hash_module =
        compile_module(&address, &hash_module, &[]).expect("hash must compile");

    let account_module = account_module();

    let mut deps = vec![compiled_coin_module, compiled_hash_module];
    let compiled_account_module =
        compile_module(&address, &account_module, &deps).expect("account must compile");

    deps.push(compiled_account_module);
    deps
}
<commit_msg>[vm_runtime_tests] add richer API for compiler<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

//! Support for compiling scripts and modules in tests.

use compiler::{
    compiler::{compile_module, compile_program as compiler_compile_program},
    parser::parse_program,
};
use std::mem;
use stdlib::stdlib::*;
use types::{
    account_address::AccountAddress,
    transaction::{Program, TransactionArgument},
};
use vm::file_format::{CompiledModule, CompiledProgram};

/// An API for the compiler. Supports setting custom options.
#[derive(Clone, Debug, Default)]
pub struct Compiler<'a> {
    /// The address used as the sender for the compiler.
    pub address: AccountAddress,
    /// The Move IR code to compile.
    pub code: &'a str,
    /// Skip stdlib dependencies if true.
    pub skip_stdlib_deps: bool,
    /// The address to use for stdlib.
    pub stdlib_address: AccountAddress,
    /// Extra dependencies to compile with.
    pub extra_deps: Vec<CompiledModule>,

    // The typical way this should be used is with functional record update syntax:
    //
    // let compiler = Compiler { address, code, ..Compiler::new() };
    //
    // Until the #[non_exhaustive] attribute is available (see
    // https://github.com/rust-lang/rust/issues/44109), this workaround is required to make the
    // syntax be mandatory.
    #[allow(missing_docs)]
    #[doc(hidden)]
    pub _non_exhaustive: (),
}

impl<'a> Compiler<'a> {
    /// Compiles into a `CompiledProgram` where the bytecode hasn't been serialized.
    pub fn into_compiled_program(mut self) -> CompiledProgram {
        self.compile_impl()
    }

    /// Compiles the script into a serialized form.
    pub fn into_script_blob(mut self) -> Vec<u8> {
        let compiled_program = self.compile_impl();

        let mut serialized_script = Vec::<u8>::new();
        compiled_program
            .script
            .serialize(&mut serialized_script)
            .expect("script must serialize");
        serialized_script
    }

    /// Compiles the code and arguments into a `Program` -- the bytecode is serialized.
    pub fn into_program(mut self, args: Vec<TransactionArgument>) -> Program {
        let compiled_program = self.compile_impl();

        let mut serialized_script = Vec::<u8>::new();
        compiled_program
            .script
            .serialize(&mut serialized_script)
            .expect("script must serialize");
        let mut serialized_modules = vec![];
        for m in compiled_program.modules {
            let mut module = vec![];
            m.serialize(&mut module).expect("module must serialize");
            serialized_modules.push(module);
        }
        Program::new(serialized_script, serialized_modules, args)
    }

    fn compile_impl(&mut self) -> CompiledProgram {
        let parsed_program = parse_program(self.code).expect("program must parse");
        let deps = self.deps();
        compiler_compile_program(&self.address, &parsed_program, &deps)
            .expect("program must compile")
    }

    fn deps(&mut self) -> Vec<CompiledModule> {
        let extra_deps = mem::replace(&mut self.extra_deps, vec![]);
        if self.skip_stdlib_deps {
            extra_deps
        } else {
            let mut deps = stdlib_deps(&self.stdlib_address);
            deps.extend(extra_deps);
            deps
        }
    }
}

// The functions below are legacy functions -- feel free to use them if convenient, but advanced
// functionality should go through Compiler above.

/// Compile the provided Move code into a blob which can be used as the code for a [`Program`].
///
/// The script is compiled with the default account address (`0x0`).
pub fn compile_script(code: &str) -> Vec<u8> {
    let compiler = Compiler {
        code,
        ..Compiler::default()
    };
    compiler.into_script_blob()
}

/// Compile the provided Move code and arguments into a `Program` using `address` as the
/// self address for any modules in `code`.
pub fn compile_program_with_address(
    address: &AccountAddress,
    code: &str,
    args: Vec<TransactionArgument>,
) -> Program {
    let compiler = Compiler {
        address: *address,
        code,
        ..Compiler::default()
    };
    compiler.into_program(args)
}

/// Compile the provided Move code and arguments into a `Program`.
///
/// This supports both scripts and modules defined in the same Move code. The code is compiled with
/// the default account address (`0x0`).
pub fn compile_program(code: &str, args: Vec<TransactionArgument>) -> Program {
    let compiler = Compiler {
        code,
        ..Compiler::default()
    };
    compiler.into_program(args)
}

fn stdlib_deps(address: &AccountAddress) -> Vec<CompiledModule> {
    let coin_module = coin_module();
    let compiled_coin_module =
        compile_module(&address, &coin_module, &[]).expect("coin must compile");

    let hash_module = native_hash_module();
    let compiled_hash_module =
        compile_module(&address, &hash_module, &[]).expect("hash must compile");

    let account_module = account_module();

    let mut deps = vec![compiled_coin_module, compiled_hash_module];
    let compiled_account_module =
        compile_module(&address, &account_module, &deps).expect("account must compile");

    deps.push(compiled_account_module);
    deps
}
<|endoftext|>