<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.
 */

//! Utilities for providing the completion language feature
use crate::lsp::{
    CompletionItem, CompletionParams, CompletionResponse, Message, ServerRequestId, ServerResponse,
    Url,
};
use crate::type_path::{TypePath, TypePathItem};
use crate::utils::extract_executable_document_from_text;
use common::Span;
use crossbeam::Sender;
use graphql_ir::Program;
use graphql_syntax::{
    Directive, DirectiveLocation, ExecutableDefinition, FragmentSpread, InlineFragment,
    LinkedField, List, OperationDefinition, OperationKind, ScalarField, Selection,
};
use graphql_syntax::{ExecutableDocument, GraphQLSource};
use interner::StringKey;
use log::info;
use relay_compiler::FileCategorizer;
use schema::{Directive as SchemaDirective, Schema, Type, TypeReference, TypeWithFields};
use std::{
    collections::HashMap,
    path::PathBuf,
    sync::{Arc, RwLock},
};

#[derive(Debug, Copy, Clone)]
pub enum CompletionKind {
    FieldName,
    FragmentSpread,
    DirectiveName { location: DirectiveLocation },
}
#[derive(Debug)]
pub struct CompletionRequest {
    /// The type of the completion request we're responding to
    kind: CompletionKind,
    /// A list of type metadata that we can use to resolve the leaf
    /// type the request is being made against
    type_path: TypePath,
    /// The project the request belongs to
    pub project_name: StringKey,
}

impl CompletionRequest {
    fn new(project_name: StringKey) -> Self {
        Self {
            kind: CompletionKind::FieldName,
            type_path: Default::default(),
            project_name,
        }
    }
}

pub fn create_completion_request(
    document: ExecutableDocument,
    position_span: Span,
    project_name: StringKey,
) -> Option<CompletionRequest> {
    info!("Building completion path for {:#?}", document);
    let mut completion_request = CompletionRequest::new(project_name);

    for definition in document.definitions {
        match &definition {
            ExecutableDefinition::Operation(operation) => {
                if operation.location.contains(position_span) {
                    let (_, kind) = operation.operation.clone()?;
                    completion_request
                        .type_path
                        .add_type(TypePathItem::Operation(kind));

                    info!(
                        "Completion request is within operation: {:?}",
                        operation.name
                    );
                    let OperationDefinition {
                        selections,
                        directives,
                        ..
                    } = operation;

                    let directive_location = match kind {
                        OperationKind::Query => DirectiveLocation::Query,
                        OperationKind::Mutation => DirectiveLocation::Mutation,
                        OperationKind::Subscription => DirectiveLocation::Subscription,
                    };

                    build_request_from_selection_or_directives(
                        selections,
                        directives,
                        directive_location,
                        position_span,
                        &mut completion_request,
                    );
                }
                // Check if the position span is within this operation's span
            }
            ExecutableDefinition::Fragment(fragment) => {
                if fragment.location.contains(position_span) {
                    let type_name = fragment.type_condition.type_.value;
                    completion_request
                        .type_path
                        .add_type(TypePathItem::FragmentDefinition { type_name });
                    build_request_from_selection_or_directives(
                        &fragment.selections,
                        &fragment.directives,
                        DirectiveLocation::FragmentDefinition,
                        position_span,
                        &mut completion_request,
                    );
                }
            }
        }
    }
    Some(completion_request)
}

fn resolve_completion_items_from_fields<T: TypeWithFields>(
    type_: &T,
    schema: &Schema,
) -> Vec<CompletionItem> {
    type_
        .fields()
        .iter()
        .map(|field_id| {
            let field = schema.field(*field_id);
            let name = field.name.to_string();
            CompletionItem::new_simple(name, String::from(""))
        })
        .collect()
}

fn resolve_completion_items_for_fragment_spread(
    type_: Type,
    source_program: &Program,
    schema: &Schema,
) -> Vec<CompletionItem> {
    let mut valid_fragments = vec![];
    for fragment in source_program.fragments() {
        if fragment.type_condition == type_ && !fragment.variable_definitions.is_empty() {
            // Create a snippet if the fragment has required arugmentDefinition with no default values
            let mut cursor_location = 1;
            let mut args = vec![];
            for arg in fragment.variable_definitions.iter() {
                if arg.default_value.is_none() {
                    if let TypeReference::NonNull(type_) = &arg.type_ {
                        let value_snippet = match type_ {
                            t if t.is_list() => format!("[${}]", cursor_location),
                            t if schema.is_string(t.inner()) => format!("\"${}\"", cursor_location),
                            _ => format!("${}", cursor_location),
                        };
                        let str = format!("{}: {}", arg.name.item, value_snippet);
                        args.push(str);
                        cursor_location += 1;
                    }
                }
            }

            valid_fragments.push(if args.is_empty() {
                CompletionItem::new_simple(fragment.name.item.to_string(), "".into())
            } else {
                let label = fragment.name.item.to_string();
                let insert_text = format!("{} @arguments({})", label, args.join(", "));
                CompletionItem {
                    label,
                    kind: None,
                    detail: None,
                    documentation: None,
                    deprecated: None,
                    preselect: None,
                    sort_text: None,
                    filter_text: None,
                    insert_text: Some(insert_text),
                    insert_text_format: Some(lsp_types::InsertTextFormat::Snippet),
                    text_edit: None,
                    additional_text_edits: None,
                    command: None,
                    data: None,
                    tags: None,
                }
            });
        }
    }
    info!("get_valid_fragments_for_type {:#?}", valid_fragments);
    valid_fragments
}

pub fn completion_items_for_request(
    request: CompletionRequest,
    schema: &Schema,
    source_programs: &Arc<RwLock<HashMap<StringKey, Program>>>,
) -> Option<Vec<CompletionItem>> {
    let kind = request.kind;
    let project_name = request.project_name;
    let leaf_type = request.type_path.resolve_leaf_type(schema)?;
    info!("completion_items_for_request: {:?} - {:?}", leaf_type, kind);
    match kind {
        CompletionKind::FragmentSpread => {
            if let Some(source_program) = source_programs.read().unwrap().get(&project_name) {
                info!("has source program");
                let items =
                    resolve_completion_items_for_fragment_spread(leaf_type, source_program, schema);
                Some(items)
            } else {
                None
            }
        }
        CompletionKind::FieldName => match leaf_type {
            Type::Interface(interface_id) => {
                let interface = schema.interface(interface_id);
                let items = resolve_completion_items_from_fields(interface, schema);
                Some(items)
            }
            Type::Object(object_id) => {
                let object = schema.object(object_id);
                let items = resolve_completion_items_from_fields(object, schema);
                Some(items)
            }
            Type::Enum(_) | Type::InputObject(_) | Type::Scalar(_) | Type::Union(_) => None,
        },
        CompletionKind::DirectiveName { location } => {
            let directives = schema.directives_for_location(location);
            let items = directives
                .iter()
                .map(|directive| completion_item_from_directive(directive, schema))
                .collect();
            Some(items)
        }
    }
}

fn build_request_from_selections(
    selections: &List<Selection>,
    position_span: Span,
    completion_request: &mut CompletionRequest,
) {
    for item in &selections.items {
        if item.span().contains(position_span) {
            match item {
                Selection::LinkedField(node) => {
                    completion_request.kind = CompletionKind::FieldName;
                    let LinkedField {
                        name,
                        selections,
                        directives,
                        ..
                    } = node;
                    completion_request
                        .type_path
                        .add_type(TypePathItem::LinkedField { name: name.value });
                    build_request_from_selection_or_directives(
                        selections,
                        directives,
                        DirectiveLocation::Field,
                        position_span,
                        completion_request,
                    );
                }
                Selection::FragmentSpread(spread) => {
                    let FragmentSpread {
                        name, directives, ..
                    } = spread;
                    if name.span.contains(position_span) {
                        completion_request.kind = CompletionKind::FragmentSpread;
                    } else {
                        build_request_from_directives(
                            directives,
                            DirectiveLocation::FragmentSpread,
                            position_span,
                            completion_request,
                        );
                    }
                }
                Selection::InlineFragment(node) => {
                    let InlineFragment {
                        selections,
                        directives,
                        type_condition,
                        ..
                    } = node;
                    if let Some(type_condition) = type_condition {
                        let type_name = type_condition.type_.value;
                        completion_request
                            .type_path
                            .add_type(TypePathItem::InlineFragment { type_name });
                        build_request_from_selection_or_directives(
                            selections,
                            directives,
                            DirectiveLocation::InlineFragment,
                            position_span,
                            completion_request,
                        )
                    }
                }
                Selection::ScalarField(node) => {
                    let ScalarField {
                        directives, name, ..
                    } = node;
                    completion_request
                        .type_path
                        .add_type(TypePathItem::ScalarField { name: name.value });
                    build_request_from_directives(
                        directives,
                        DirectiveLocation::Scalar,
                        position_span,
                        completion_request,
                    );
                }
            }
        }
    }
}

fn build_request_from_directives(
    directives: &[Directive],
    location: DirectiveLocation,
    position_span: Span,
    completion_request: &mut CompletionRequest,
) {
    for Directive { span, .. } in directives {
        if span.contains(position_span) {
            completion_request.kind = CompletionKind::DirectiveName { location };
            break;
        }
    }
}

fn build_request_from_selection_or_directives(
    selections: &List<Selection>,
    directives: &[Directive],
    directive_location: DirectiveLocation,
    position_span: Span,
    completion_request: &mut CompletionRequest,
) {
    if selections.span.contains(position_span) {
        // TODO(brandondail) handle when the completion occurs at/within the start token
        build_request_from_selections(selections, position_span, completion_request);
    } else {
        build_request_from_directives(
            directives,
            directive_location,
            position_span,
            completion_request,
        )
    }
}

fn completion_item_from_directive(directive: &SchemaDirective, schema: &Schema) -> CompletionItem {
    let SchemaDirective {
        name, arguments, ..
    } = directive;

    use crate::lsp::InsertTextFormat;

    // Always use the name of the directive as the label
    let label = name.to_string();

    // We can return a snippet with the expected arguments of the directive
    let (insert_text, insert_text_format) = if arguments.is_empty() {
        (label.clone(), InsertTextFormat::PlainText)
    } else {
        let mut cursor_location = 1;
        let mut args = vec![];

        for arg in arguments.iter() {
            if let TypeReference::NonNull(type_) = &arg.type_ {
                let value_snippet = match type_ {
                    t if t.is_list() => format!("[${}]", cursor_location),
                    t if schema.is_string(t.inner()) => format!("\"${}\"", cursor_location),
                    _ => format!("${}", cursor_location),
                };
                let str = format!("{} : {}", arg.name, value_snippet);
                args.push(str);
                cursor_location += 1;
            }
        }

        if args.is_empty() {
            (label.clone(), InsertTextFormat::PlainText)
        } else {
            let insert_text = format!("{}({})", label, args.join(", "));
            (insert_text, InsertTextFormat::Snippet)
        }
    };

    CompletionItem {
        label,
        kind: None,
        detail: None,
        documentation: None,
        deprecated: None,
        preselect: None,
        sort_text: None,
        filter_text: None,
        insert_text: Some(insert_text),
        insert_text_format: Some(insert_text_format),
        text_edit: None,
        additional_text_edits: None,
        command: None,
        data: None,
        tags: None,
    }
}

pub fn send_completion_response(
    items: Vec<CompletionItem>,
    request_id: ServerRequestId,
    sender: &Sender<Message>,
) {
    // If there are no items, don't send any response
    if items.is_empty() {
        return;
    }
    let completion_response = CompletionResponse::Array(items);
    let result = serde_json::to_value(&completion_response).ok();
    let response = ServerResponse {
        id: request_id,
        error: None,
        result,
    };
    sender.send(Message::Response(response)).ok();
}

/// Return a `CompletionPath` for this request, only if the completion request occurs
/// within a GraphQL document. Otherwise return `None`
pub fn get_completion_request(
    params: CompletionParams,
    graphql_source_cache: &HashMap<Url, Vec<GraphQLSource>>,
    file_categorizer: &FileCategorizer,
    root_dir: &PathBuf,
) -> Option<CompletionRequest> {
    let CompletionParams {
        text_document_position,
        ..
    } = params;

    let (document, position_span, project_name) = extract_executable_document_from_text(
        text_document_position,
        graphql_source_cache,
        file_categorizer,
        root_dir,
    )
    .ok()?;

    let completion_request = create_completion_request(document, position_span, project_name);
    info!("Completion request: {:#?}", completion_request);
    completion_request
}
<commit_msg>Add `{}` when auto complet linked fields<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.
 */

//! Utilities for providing the completion language feature
use crate::lsp::{
    CompletionItem, CompletionParams, CompletionResponse, Message, ServerRequestId, ServerResponse,
    Url,
};
use crate::type_path::{TypePath, TypePathItem};
use crate::utils::extract_executable_document_from_text;
use common::Span;
use crossbeam::Sender;
use graphql_ir::Program;
use graphql_syntax::{
    Directive, DirectiveLocation, ExecutableDefinition, FragmentSpread, InlineFragment,
    LinkedField, List, OperationDefinition, OperationKind, ScalarField, Selection,
};
use graphql_syntax::{ExecutableDocument, GraphQLSource};
use interner::StringKey;
use log::info;
use relay_compiler::FileCategorizer;
use schema::{Directive as SchemaDirective, Schema, Type, TypeReference, TypeWithFields};
use std::{
    collections::HashMap,
    path::PathBuf,
    sync::{Arc, RwLock},
};

#[derive(Debug, Copy, Clone)]
pub enum CompletionKind {
    FieldName,
    FragmentSpread,
    DirectiveName { location: DirectiveLocation },
}
#[derive(Debug)]
pub struct CompletionRequest {
    /// The type of the completion request we're responding to
    kind: CompletionKind,
    /// A list of type metadata that we can use to resolve the leaf
    /// type the request is being made against
    type_path: TypePath,
    /// The project the request belongs to
    pub project_name: StringKey,
}

impl CompletionRequest {
    fn new(project_name: StringKey) -> Self {
        Self {
            kind: CompletionKind::FieldName,
            type_path: Default::default(),
            project_name,
        }
    }
}

pub fn create_completion_request(
    document: ExecutableDocument,
    position_span: Span,
    project_name: StringKey,
) -> Option<CompletionRequest> {
    info!("Building completion path for {:#?}", document);
    let mut completion_request = CompletionRequest::new(project_name);

    for definition in document.definitions {
        match &definition {
            ExecutableDefinition::Operation(operation) => {
                if operation.location.contains(position_span) {
                    let (_, kind) = operation.operation.clone()?;
                    completion_request
                        .type_path
                        .add_type(TypePathItem::Operation(kind));

                    info!(
                        "Completion request is within operation: {:?}",
                        operation.name
                    );
                    let OperationDefinition {
                        selections,
                        directives,
                        ..
                    } = operation;

                    let directive_location = match kind {
                        OperationKind::Query => DirectiveLocation::Query,
                        OperationKind::Mutation => DirectiveLocation::Mutation,
                        OperationKind::Subscription => DirectiveLocation::Subscription,
                    };

                    build_request_from_selection_or_directives(
                        selections,
                        directives,
                        directive_location,
                        position_span,
                        &mut completion_request,
                    );
                }
                // Check if the position span is within this operation's span
            }
            ExecutableDefinition::Fragment(fragment) => {
                if fragment.location.contains(position_span) {
                    let type_name = fragment.type_condition.type_.value;
                    completion_request
                        .type_path
                        .add_type(TypePathItem::FragmentDefinition { type_name });
                    build_request_from_selection_or_directives(
                        &fragment.selections,
                        &fragment.directives,
                        DirectiveLocation::FragmentDefinition,
                        position_span,
                        &mut completion_request,
                    );
                }
            }
        }
    }
    Some(completion_request)
}

fn resolve_completion_items_from_fields<T: TypeWithFields>(
    type_: &T,
    schema: &Schema,
) -> Vec<CompletionItem> {
    type_
        .fields()
        .iter()
        .map(|field_id| {
            let field = schema.field(*field_id);
            let name = field.name.to_string();
            if field.type_.inner().is_scalar() {
                CompletionItem::new_simple(name, "".into())
            } else {
                let insert_text = format!("{} {{\n\t$1\n}}", name);
                CompletionItem {
                    label: name,
                    kind: None,
                    detail: None,
                    documentation: None,
                    deprecated: None,
                    preselect: None,
                    sort_text: None,
                    filter_text: None,
                    insert_text: Some(insert_text),
                    insert_text_format: Some(lsp_types::InsertTextFormat::Snippet),
                    text_edit: None,
                    additional_text_edits: None,
                    command: None,
                    data: None,
                    tags: None,
                }
            }
        })
        .collect()
}

fn resolve_completion_items_for_fragment_spread(
    type_: Type,
    source_program: &Program,
    schema: &Schema,
) -> Vec<CompletionItem> {
    let mut valid_fragments = vec![];
    for fragment in source_program.fragments() {
        if fragment.type_condition == type_ && !fragment.variable_definitions.is_empty() {
            // Create a snippet if the fragment has required arugmentDefinition with no default values
            let mut cursor_location = 1;
            let mut args = vec![];
            for arg in fragment.variable_definitions.iter() {
                if arg.default_value.is_none() {
                    if let TypeReference::NonNull(type_) = &arg.type_ {
                        let value_snippet = match type_ {
                            t if t.is_list() => format!("[${}]", cursor_location),
                            t if schema.is_string(t.inner()) => format!("\"${}\"", cursor_location),
                            _ => format!("${}", cursor_location),
                        };
                        let str = format!("{}: {}", arg.name.item, value_snippet);
                        args.push(str);
                        cursor_location += 1;
                    }
                }
            }

            valid_fragments.push(if args.is_empty() {
                CompletionItem::new_simple(fragment.name.item.to_string(), "".into())
            } else {
                let label = fragment.name.item.to_string();
                let insert_text = format!("{} @arguments({})", label, args.join(", "));
                CompletionItem {
                    label,
                    kind: None,
                    detail: None,
                    documentation: None,
                    deprecated: None,
                    preselect: None,
                    sort_text: None,
                    filter_text: None,
                    insert_text: Some(insert_text),
                    insert_text_format: Some(lsp_types::InsertTextFormat::Snippet),
                    text_edit: None,
                    additional_text_edits: None,
                    command: None,
                    data: None,
                    tags: None,
                }
            });
        }
    }
    info!("get_valid_fragments_for_type {:#?}", valid_fragments);
    valid_fragments
}

pub fn completion_items_for_request(
    request: CompletionRequest,
    schema: &Schema,
    source_programs: &Arc<RwLock<HashMap<StringKey, Program>>>,
) -> Option<Vec<CompletionItem>> {
    let kind = request.kind;
    let project_name = request.project_name;
    let leaf_type = request.type_path.resolve_leaf_type(schema)?;
    info!("completion_items_for_request: {:?} - {:?}", leaf_type, kind);
    match kind {
        CompletionKind::FragmentSpread => {
            if let Some(source_program) = source_programs.read().unwrap().get(&project_name) {
                info!("has source program");
                let items =
                    resolve_completion_items_for_fragment_spread(leaf_type, source_program, schema);
                Some(items)
            } else {
                None
            }
        }
        CompletionKind::FieldName => match leaf_type {
            Type::Interface(interface_id) => {
                let interface = schema.interface(interface_id);
                let items = resolve_completion_items_from_fields(interface, schema);
                Some(items)
            }
            Type::Object(object_id) => {
                let object = schema.object(object_id);
                let items = resolve_completion_items_from_fields(object, schema);
                Some(items)
            }
            Type::Enum(_) | Type::InputObject(_) | Type::Scalar(_) | Type::Union(_) => None,
        },
        CompletionKind::DirectiveName { location } => {
            let directives = schema.directives_for_location(location);
            let items = directives
                .iter()
                .map(|directive| completion_item_from_directive(directive, schema))
                .collect();
            Some(items)
        }
    }
}

fn build_request_from_selections(
    selections: &List<Selection>,
    position_span: Span,
    completion_request: &mut CompletionRequest,
) {
    for item in &selections.items {
        if item.span().contains(position_span) {
            match item {
                Selection::LinkedField(node) => {
                    completion_request.kind = CompletionKind::FieldName;
                    let LinkedField {
                        name,
                        selections,
                        directives,
                        ..
                    } = node;
                    completion_request
                        .type_path
                        .add_type(TypePathItem::LinkedField { name: name.value });
                    build_request_from_selection_or_directives(
                        selections,
                        directives,
                        DirectiveLocation::Field,
                        position_span,
                        completion_request,
                    );
                }
                Selection::FragmentSpread(spread) => {
                    let FragmentSpread {
                        name, directives, ..
                    } = spread;
                    if name.span.contains(position_span) {
                        completion_request.kind = CompletionKind::FragmentSpread;
                    } else {
                        build_request_from_directives(
                            directives,
                            DirectiveLocation::FragmentSpread,
                            position_span,
                            completion_request,
                        );
                    }
                }
                Selection::InlineFragment(node) => {
                    let InlineFragment {
                        selections,
                        directives,
                        type_condition,
                        ..
                    } = node;
                    if let Some(type_condition) = type_condition {
                        let type_name = type_condition.type_.value;
                        completion_request
                            .type_path
                            .add_type(TypePathItem::InlineFragment { type_name });
                        build_request_from_selection_or_directives(
                            selections,
                            directives,
                            DirectiveLocation::InlineFragment,
                            position_span,
                            completion_request,
                        )
                    }
                }
                Selection::ScalarField(node) => {
                    let ScalarField {
                        directives, name, ..
                    } = node;
                    completion_request
                        .type_path
                        .add_type(TypePathItem::ScalarField { name: name.value });
                    build_request_from_directives(
                        directives,
                        DirectiveLocation::Scalar,
                        position_span,
                        completion_request,
                    );
                }
            }
        }
    }
}

fn build_request_from_directives(
    directives: &[Directive],
    location: DirectiveLocation,
    position_span: Span,
    completion_request: &mut CompletionRequest,
) {
    for Directive { span, .. } in directives {
        if span.contains(position_span) {
            completion_request.kind = CompletionKind::DirectiveName { location };
            break;
        }
    }
}

fn build_request_from_selection_or_directives(
    selections: &List<Selection>,
    directives: &[Directive],
    directive_location: DirectiveLocation,
    position_span: Span,
    completion_request: &mut CompletionRequest,
) {
    if selections.span.contains(position_span) {
        // TODO(brandondail) handle when the completion occurs at/within the start token
        build_request_from_selections(selections, position_span, completion_request);
    } else {
        build_request_from_directives(
            directives,
            directive_location,
            position_span,
            completion_request,
        )
    }
}

fn completion_item_from_directive(directive: &SchemaDirective, schema: &Schema) -> CompletionItem {
    let SchemaDirective {
        name, arguments, ..
    } = directive;

    use crate::lsp::InsertTextFormat;

    // Always use the name of the directive as the label
    let label = name.to_string();

    // We can return a snippet with the expected arguments of the directive
    let (insert_text, insert_text_format) = if arguments.is_empty() {
        (label.clone(), InsertTextFormat::PlainText)
    } else {
        let mut cursor_location = 1;
        let mut args = vec![];

        for arg in arguments.iter() {
            if let TypeReference::NonNull(type_) = &arg.type_ {
                let value_snippet = match type_ {
                    t if t.is_list() => format!("[${}]", cursor_location),
                    t if schema.is_string(t.inner()) => format!("\"${}\"", cursor_location),
                    _ => format!("${}", cursor_location),
                };
                let str = format!("{} : {}", arg.name, value_snippet);
                args.push(str);
                cursor_location += 1;
            }
        }

        if args.is_empty() {
            (label.clone(), InsertTextFormat::PlainText)
        } else {
            let insert_text = format!("{}({})", label, args.join(", "));
            (insert_text, InsertTextFormat::Snippet)
        }
    };

    CompletionItem {
        label,
        kind: None,
        detail: None,
        documentation: None,
        deprecated: None,
        preselect: None,
        sort_text: None,
        filter_text: None,
        insert_text: Some(insert_text),
        insert_text_format: Some(insert_text_format),
        text_edit: None,
        additional_text_edits: None,
        command: None,
        data: None,
        tags: None,
    }
}

pub fn send_completion_response(
    items: Vec<CompletionItem>,
    request_id: ServerRequestId,
    sender: &Sender<Message>,
) {
    // If there are no items, don't send any response
    if items.is_empty() {
        return;
    }
    let completion_response = CompletionResponse::Array(items);
    let result = serde_json::to_value(&completion_response).ok();
    let response = ServerResponse {
        id: request_id,
        error: None,
        result,
    };
    sender.send(Message::Response(response)).ok();
}

/// Return a `CompletionPath` for this request, only if the completion request occurs
/// within a GraphQL document. Otherwise return `None`
pub fn get_completion_request(
    params: CompletionParams,
    graphql_source_cache: &HashMap<Url, Vec<GraphQLSource>>,
    file_categorizer: &FileCategorizer,
    root_dir: &PathBuf,
) -> Option<CompletionRequest> {
    let CompletionParams {
        text_document_position,
        ..
    } = params;

    let (document, position_span, project_name) = extract_executable_document_from_text(
        text_document_position,
        graphql_source_cache,
        file_categorizer,
        root_dir,
    )
    .ok()?;

    let completion_request = create_completion_request(document, position_span, project_name);
    info!("Completion request: {:#?}", completion_request);
    completion_request
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

use syntax::ast;
use syntax::attr;
use syntax::ext::base::ExtCtxt;
use syntax::ptr::P;

use aster;

use super::attr::FieldAttrs;

enum Rename<'a> {
    None,
    Global(&'a ast::Lit),
    Format(HashMap<P<ast::Expr>, &'a ast::Lit>)
}

fn rename<'a>(
    builder: &aster::AstBuilder,
    mi: &'a ast::MetaItem,
    ) -> Option<Rename<'a>>
{
    match mi.node {
        ast::MetaNameValue(ref n, ref lit) => {
            if n == &"rename" {
                Some(Rename::Global(lit))
            } else {
                None
            }
        },
        ast::MetaList(ref n, ref items) => {
            if n == &"rename" {
                let mut m = HashMap::new();
                m.extend(
                    items.iter()
                        .filter_map(
                            |item|
                            match item.node {
                                ast::MetaNameValue(ref n, ref lit) =>
                                    Some((builder.expr().str(n),
                                          lit)),
                                _ => None
                            }));
                Some(Rename::Format(m))
            } else {
                None
            }
        },
        _ => None
    }
}

pub fn default_value(mi: &ast::MetaItem) -> bool {
    if let ast::MetaItem_::MetaWord(ref n) = mi.node {
        n == &"default"
    } else {
        false
    }
}

fn field_attrs<'a>(
    builder: &aster::AstBuilder,
    field: &'a ast::StructField,
) -> (Rename<'a>, bool) {
    field.node.attrs.iter()
        .find(|sa| {
            if let ast::MetaList(ref n, _) = sa.node.value.node {
                n == &"serde"
            } else {
                false
            }
        })
        .and_then(|sa| {
            if let ast::MetaList(_, ref vals) = sa.node.value.node {
                attr::mark_used(&sa);
                Some((vals.iter()
                      .fold(None, |v, mi| v.or(rename(builder, mi)))
                      .unwrap_or(Rename::None),
                      vals.iter().any(|mi| default_value(mi))))
            } else {
                Some((Rename::None, false))
            }
        })
        .unwrap_or((Rename::None, false))
}

pub fn struct_field_attrs(
    cx: &ExtCtxt,
    builder: &aster::AstBuilder,
    struct_def: &ast::StructDef,
) -> Vec<FieldAttrs> {
    struct_def.fields.iter()
        .map(|field| {
            match field_attrs(builder, field) {
                (Rename::Global(rename), default_value) =>
                    FieldAttrs::new(
                        default_value,
                        builder.expr().build_lit(P(rename.clone()))),
                (Rename::Format(renames), default_value) => {
                    let mut res = HashMap::new();
                    res.extend(
                        renames.into_iter()
                            .map(|(k,v)|
                                 (k, builder.expr().build_lit(P(v.clone())))));
                    FieldAttrs::new_with_formats(
                        default_value,
                        default_field_name(cx, builder, field.node.kind),
                        res)
                },
                (Rename::None, default_value) => {
                    FieldAttrs::new(
                        default_value,
                        default_field_name(cx, builder, field.node.kind))
                }
            }
        })
        .collect()
}

fn default_field_name(
    cx: &ExtCtxt,
    builder: &aster::AstBuilder,
    kind: ast::StructFieldKind,
) -> P<ast::Expr> {
    match kind {
        ast::NamedField(name, _) => {
            builder.expr().str(name)
        }
        ast::UnnamedField(_) => {
            cx.bug("struct has named and unnamed fields")
        }
    }
}
<commit_msg>default_value doesn't need to be public<commit_after>use std::collections::HashMap;

use syntax::ast;
use syntax::attr;
use syntax::ext::base::ExtCtxt;
use syntax::ptr::P;

use aster;

use super::attr::FieldAttrs;

enum Rename<'a> {
    None,
    Global(&'a ast::Lit),
    Format(HashMap<P<ast::Expr>, &'a ast::Lit>)
}

fn rename<'a>(
    builder: &aster::AstBuilder,
    mi: &'a ast::MetaItem,
    ) -> Option<Rename<'a>>
{
    match mi.node {
        ast::MetaNameValue(ref n, ref lit) => {
            if n == &"rename" {
                Some(Rename::Global(lit))
            } else {
                None
            }
        },
        ast::MetaList(ref n, ref items) => {
            if n == &"rename" {
                let mut m = HashMap::new();
                m.extend(
                    items.iter()
                        .filter_map(
                            |item|
                            match item.node {
                                ast::MetaNameValue(ref n, ref lit) =>
                                    Some((builder.expr().str(n),
                                          lit)),
                                _ => None
                            }));
                Some(Rename::Format(m))
            } else {
                None
            }
        },
        _ => None
    }
}

fn default_value(mi: &ast::MetaItem) -> bool {
    if let ast::MetaItem_::MetaWord(ref n) = mi.node {
        n == &"default"
    } else {
        false
    }
}

fn field_attrs<'a>(
    builder: &aster::AstBuilder,
    field: &'a ast::StructField,
) -> (Rename<'a>, bool) {
    field.node.attrs.iter()
        .find(|sa| {
            if let ast::MetaList(ref n, _) = sa.node.value.node {
                n == &"serde"
            } else {
                false
            }
        })
        .and_then(|sa| {
            if let ast::MetaList(_, ref vals) = sa.node.value.node {
                attr::mark_used(&sa);
                Some((vals.iter()
                      .fold(None, |v, mi| v.or(rename(builder, mi)))
                      .unwrap_or(Rename::None),
                      vals.iter().any(|mi| default_value(mi))))
            } else {
                Some((Rename::None, false))
            }
        })
        .unwrap_or((Rename::None, false))
}

pub fn struct_field_attrs(
    cx: &ExtCtxt,
    builder: &aster::AstBuilder,
    struct_def: &ast::StructDef,
) -> Vec<FieldAttrs> {
    struct_def.fields.iter()
        .map(|field| {
            match field_attrs(builder, field) {
                (Rename::Global(rename), default_value) =>
                    FieldAttrs::new(
                        default_value,
                        builder.expr().build_lit(P(rename.clone()))),
                (Rename::Format(renames), default_value) => {
                    let mut res = HashMap::new();
                    res.extend(
                        renames.into_iter()
                            .map(|(k,v)|
                                 (k, builder.expr().build_lit(P(v.clone())))));
                    FieldAttrs::new_with_formats(
                        default_value,
                        default_field_name(cx, builder, field.node.kind),
                        res)
                },
                (Rename::None, default_value) => {
                    FieldAttrs::new(
                        default_value,
                        default_field_name(cx, builder, field.node.kind))
                }
            }
        })
        .collect()
}

fn default_field_name(
    cx: &ExtCtxt,
    builder: &aster::AstBuilder,
    kind: ast::StructFieldKind,
) -> P<ast::Expr> {
    match kind {
        ast::NamedField(name, _) => {
            builder.expr().str(name)
        }
        ast::UnnamedField(_) => {
            cx.bug("struct has named and unnamed fields")
        }
    }
}
<|endoftext|>


<commit_before>#![feature(test, proc_macro, rustc_attrs)]

#[macro_use]
extern crate serde_derive;

extern crate test;

include!("../../testing/tests/test.rs.in");

mod compile_tests;
<commit_msg>Remove unneeded test feature<commit_after>#![feature(test, proc_macro)]

#[macro_use]
extern crate serde_derive;

extern crate test;

include!("../../testing/tests/test.rs.in");

mod compile_tests;
<|endoftext|>


<commit_before>use quickcheck;
use quickcheck::{Arbitrary, Gen};
use field::Pos;
use hash_table::{HashType, HashData};

#[derive(Clone, Copy, PartialEq, Debug)]
struct HashTypeArbitrary {
  hash_type: HashType
}

impl Arbitrary for HashTypeArbitrary {
  fn arbitrary<G: Gen>(gen: &mut G) -> HashTypeArbitrary {
    HashTypeArbitrary {
      hash_type: HashType::from((gen.next_u32() % 4) as usize)
    }
  }
}

#[test]
fn hash_data_check() {
  #[cfg_attr(feature="clippy", allow(needless_pass_by_value))]
  fn prop(depth: u32, hash_type_arbitrary: HashTypeArbitrary, pos: Pos, estimation: i32) -> bool {
    let hash_data = HashData::new(depth, hash_type_arbitrary.hash_type, pos, estimation);
    hash_data.depth() == depth &&
      hash_data.hash_type() == hash_type_arbitrary.hash_type &&
      hash_data.pos() == pos &&
      hash_data.estimation() == estimation
  }
  quickcheck::quickcheck(prop as fn(u32, HashTypeArbitrary, Pos, i32) -> bool);
}
<commit_msg>Add tests.<commit_after>use quickcheck;
use quickcheck::{Arbitrary, Gen};
use field::Pos;
use hash_table::{HashType, HashData, HashTable};

#[derive(Clone, Copy, PartialEq, Debug)]
struct HashTypeArbitrary {
  hash_type: HashType
}

impl Arbitrary for HashTypeArbitrary {
  fn arbitrary<G: Gen>(gen: &mut G) -> HashTypeArbitrary {
    HashTypeArbitrary {
      hash_type: HashType::from((gen.next_u32() % 4) as usize)
    }
  }
}

#[test]
fn hash_data_check() {
  #[cfg_attr(feature="clippy", allow(needless_pass_by_value))]
  fn prop(depth: u32, hash_type_arbitrary: HashTypeArbitrary, pos: Pos, estimation: i32) -> bool {
    let hash_data = HashData::new(depth, hash_type_arbitrary.hash_type, pos, estimation);
    hash_data.depth() == depth &&
      hash_data.hash_type() == hash_type_arbitrary.hash_type &&
      hash_data.pos() == pos &&
      hash_data.estimation() == estimation
  }
  quickcheck::quickcheck(prop as fn(u32, HashTypeArbitrary, Pos, i32) -> bool);
}

#[test]
fn hash_table_put_get_one_entry() {
  let hash_table = HashTable::new(100);
  let hash = 1234567890u64;
  let data = HashData::new(3, HashType::Exact, 17, 1234);
  hash_table.put(hash, data);
  assert_eq!(hash_table.get(hash), data);
}

#[test]
fn hash_table_collision() {
  let hash_table = HashTable::new(100);
  let hash1 = 123u64;
  let hash2 = 723u64;
  let data1 = HashData::new(3, HashType::Exact, 17, 1234);
  let data2 = HashData::new(7, HashType::Alpha, 23, -4321);
  hash_table.put(hash1, data1);
  hash_table.put(hash2, data2);
  assert_eq!(hash_table.get(hash1).hash_type(), HashType::Empty);
  assert_eq!(hash_table.get(hash2), data2);
}
<|endoftext|>


<commit_before>extern crate fall_test;
extern crate fall_tree;

use fall_test::{weird, match_ast};
use fall_tree::dump_file_ws;

fn ast(code: &str) -> String {
    dump_file_ws(&weird::LANG.parse(code.to_owned()))
}

#[test]
fn external_rule() {
    match_ast(&ast(r###"_1 r##"f#"o"#o"## "###), r###"
FILE
  T1 "_1"
  WHITESPACE " "
  RAW_STRING "r##\"f#\"o\"#o\"##"
  WHITESPACE " "
"###)
}

#[test]
fn empty_nodes() {
    match_ast(&ast("_2 hello "), r#"
FILE
  T2 "_2"
  WHITESPACE " "
  EMPTY ""
  ATOM "hello"
  WHITESPACE " "
  EMPTY ""
"#)
}

#[test]
fn rollbacks_private_rules() {
    match_ast(&ast("_3 foo bar"), r#"
FILE
  T3 "_3"
  WHITESPACE " "
  PRIVATE_PARTIAL
    FOO "foo"
    WHITESPACE " "
    BAR "bar"
"#);

    match_ast(&ast("_3 foo foo"), r#"
FILE
  T3 "_3"
  WHITESPACE " "
  PRIVATE_PARTIAL
    FOO "foo"
    WHITESPACE " "
    FOO "foo"
"#);
}

#[test]
fn block1() {
    match_ast(&ast("_4 { foo }"), r#"
FILE
  T4 "_4"
  WHITESPACE " "
  BLOCK
    LBRACE "{"
    WHITESPACE " "
    FOO "foo"
    WHITESPACE " "
    RBRACE "}"
"#);
}

#[test]
fn block2() {
    match_ast(&ast("_4 { foo { {foo} foo {bar}} bar }"), r#"
FILE
  T4 "_4"
  WHITESPACE " "
  BLOCK
    LBRACE "{"
    WHITESPACE " "
    FOO "foo"
    WHITESPACE " "
    LBRACE "{"
    WHITESPACE " "
    LBRACE "{"
    FOO "foo"
    RBRACE "}"
    WHITESPACE " "
    FOO "foo"
    WHITESPACE " "
    LBRACE "{"
    BAR "bar"
    RBRACE "}"
    RBRACE "}"
    WHITESPACE " "
    BAR "bar"
    WHITESPACE " "
    RBRACE "}"
"#);
}

#[test]
fn block3() {
    match_ast(&ast("_4 {"), r#"
FILE
  T4 "_4"
  WHITESPACE " "
  BLOCK
    LBRACE "{"
    ERROR ""
"#);
}
<commit_msg>One more test<commit_after>extern crate fall_test;
extern crate fall_tree;

use fall_test::{weird, match_ast};
use fall_tree::dump_file_ws;

fn ast(code: &str) -> String {
    dump_file_ws(&weird::LANG.parse(code.to_owned()))
}

#[test]
fn external_rule() {
    match_ast(&ast(r###"_1 r##"f#"o"#o"## "###), r###"
FILE
  T1 "_1"
  WHITESPACE " "
  RAW_STRING "r##\"f#\"o\"#o\"##"
  WHITESPACE " "
"###)
}

#[test]
fn empty_nodes() {
    match_ast(&ast("_2 hello "), r#"
FILE
  T2 "_2"
  WHITESPACE " "
  EMPTY ""
  ATOM "hello"
  WHITESPACE " "
  EMPTY ""
"#)
}

#[test]
fn rollbacks_private_rules() {
    match_ast(&ast("_3 foo bar"), r#"
FILE
  T3 "_3"
  WHITESPACE " "
  PRIVATE_PARTIAL
    FOO "foo"
    WHITESPACE " "
    BAR "bar"
"#);

    match_ast(&ast("_3 foo foo"), r#"
FILE
  T3 "_3"
  WHITESPACE " "
  PRIVATE_PARTIAL
    FOO "foo"
    WHITESPACE " "
    FOO "foo"
"#);
}

#[test]
fn block1() {
    match_ast(&ast("_4 { foo }"), r#"
FILE
  T4 "_4"
  WHITESPACE " "
  BLOCK
    LBRACE "{"
    WHITESPACE " "
    FOO "foo"
    WHITESPACE " "
    RBRACE "}"
"#);
}

#[test]
fn block2() {
    match_ast(&ast("_4 { foo { {foo} foo {bar}} bar }"), r#"
FILE
  T4 "_4"
  WHITESPACE " "
  BLOCK
    LBRACE "{"
    WHITESPACE " "
    FOO "foo"
    WHITESPACE " "
    LBRACE "{"
    WHITESPACE " "
    LBRACE "{"
    FOO "foo"
    RBRACE "}"
    WHITESPACE " "
    FOO "foo"
    WHITESPACE " "
    LBRACE "{"
    BAR "bar"
    RBRACE "}"
    RBRACE "}"
    WHITESPACE " "
    BAR "bar"
    WHITESPACE " "
    RBRACE "}"
"#);
}

#[test]
fn block3() {
    match_ast(&ast("_4 {"), r#"
FILE
  T4 "_4"
  WHITESPACE " "
  BLOCK
    LBRACE "{"
    ERROR ""
"#);
}

#[test]
fn block4() {
    match_ast(&ast("_4 { foo { {} bar"), r#"
FILE
  T4 "_4"
  WHITESPACE " "
  BLOCK
    LBRACE "{"
    WHITESPACE " "
    FOO "foo"
    WHITESPACE " "
    LBRACE "{"
    WHITESPACE " "
    LBRACE "{"
    RBRACE "}"
    WHITESPACE " "
    BAR "bar"
    ERROR ""
    ERROR ""
"#);
}
<|endoftext|>


<commit_before>use pow::*;
use std::mem;
#[cfg(unix)]
use std::fs::File;
#[cfg(unix)]
use std::os::unix::io::AsRawFd;

/// Contains the Heartbeat and its window data buffer.
pub struct HeartbeatPow {
    pub hb: heartbeat_pow_context,
    pub hbr: Vec<heartbeat_pow_record>,
}

impl HeartbeatPow {
    /// Allocate and initialize a new `Heartbeat`.
    pub fn new(window_size: usize,
               hwc_callback: Option<heartbeat_pow_window_complete>) -> Result<HeartbeatPow, &'static str> {
        let mut hbr = Vec::with_capacity(window_size);
        unsafe {
            let mut hb = mem::uninitialized();
            match heartbeat_pow_init(&mut hb, hbr.capacity() as u64, hbr.as_mut_ptr(), hwc_callback) {
                0 => Ok(HeartbeatPow { hb: hb, hbr: hbr, }),
                _ => Err("Failed to initialize heartbeat")
            }
        }
    }

    /// Issue a heartbeat
    pub fn heartbeat_pow(&mut self,
                         tag: u64,
                         work: u64,
                         start_time: u64,
                         end_time: u64,
                         start_energy: u64,
                         end_energy: u64) {
        unsafe {
            heartbeat_pow(&mut self.hb, tag, work, start_time, end_time, start_energy, end_energy)
        }
    }

    #[cfg(unix)]
    pub fn log_window_buffer(&self, log: File, print_header: bool) -> Result<(), &'static str> {
        let ph: i32 = match print_header {
            true => 1,
            false => 0,
        };
        unsafe {
            match heartbeat_pow_log_window_buffer(&self.hb, log.as_raw_fd(), ph) {
                0 => Ok(()),
                _ => Err("Error logging window buffer"),
            }
        }
    }

    /// Utility function to get the most recent user-specified tag
    pub fn get_tag(&self) -> u64 {
        unsafe {
            hb_pow_get_user_tag(&self.hb)
        }
    }

    /// Utility function to get the current window performance.
    pub fn get_window_perf(&self) -> f64 {
        unsafe {
            hb_pow_get_window_perf(&self.hb)
        }
    }

    /// Utility function to get the current window power.
    pub fn get_window_pwr(&self) -> f64 {
        unsafe {
            hb_pow_get_window_power(&self.hb)
        }
    }
}

#[cfg(test)]
mod test {
    use super::HeartbeatPow;

    #[test]
    fn test_simple() {
        const TIME_INC: u64 = 1000000000;
        const ENERGY_INC: u64 = 1000000;
        let mut hb = HeartbeatPow::new(5, None).unwrap();
        let mut start_time: u64 = 0;
        let mut end_time: u64 = TIME_INC;
        let mut start_energy: u64 = 0;
        let mut end_energy: u64 = ENERGY_INC;
        let mut tag: u64 = 0;
        for _ in 0..10 {
            hb.heartbeat_pow(tag, 1, start_time, end_time, start_energy, end_energy);
            assert!(hb.get_tag() == tag);
            assert!(hb.get_window_perf() == 1.0);
            assert!(hb.get_window_pwr() == 1.0);
            tag += 1;
            start_time = end_time;
            end_time += TIME_INC;
            start_energy = end_energy;
            end_energy += ENERGY_INC;
        }
    }
}
<commit_msg>Remove native logging support in wrapper and handle in rust<commit_after>use pow::*;
use std::mem;
use std::io::{self, Write};
use std::fs::File;

/// Contains the Heartbeat and its window data buffer.
pub struct HeartbeatPow {
    pub hb: heartbeat_pow_context,
    pub hbr: Vec<heartbeat_pow_record>,
    pub log: Option<File>,
}

impl HeartbeatPow {
    /// Allocate and initialize a new `Heartbeat`.
    pub fn new(window_size: usize,
               hwc_callback: Option<heartbeat_pow_window_complete>,
               mut log: Option<File>) -> Result<HeartbeatPow, &'static str> {
        let mut hbr: Vec<heartbeat_pow_record> = Vec::with_capacity(window_size);
        unsafe {
            // must explicitly set size so we can read data later
            // (Rust isn't aware of native code modifying the buffer)
            hbr.set_len(window_size);
            let mut hb = mem::uninitialized();
            match heartbeat_pow_init(&mut hb,
                                     hbr.capacity() as u64,
                                     hbr.as_mut_ptr(),
                                     hwc_callback) {
                0 => {
                    if let Some(ref mut l) = log {
                        l.write_all("HB    Tag    Work    Start_Time    End_time    \
                            Global_Perf    Window_Perf    Instant_Perf    \
                            Start_Energy    End_Energy    \
                            Global_Pwr    Window_Pwr    Instant_Pwr\n".as_bytes()).unwrap()
                    }
                    Ok(HeartbeatPow { hb: hb, hbr: hbr, log: log, })
                },
                _ => Err("Failed to initialize heartbeat")
            }
        }
    }

    /// Issue a heartbeat
    pub fn heartbeat(&mut self,
                     tag: u64,
                     work: u64,
                     start_time: u64,
                     end_time: u64,
                     start_energy: u64,
                     end_energy: u64) {
        unsafe {
            heartbeat_pow(&mut self.hb, tag, work, start_time, end_time, start_energy, end_energy)
        }
    }

    fn write_log(r: &heartbeat_pow_record, l: &mut File) -> io::Result<usize> {
        l.write(format!("{}    {}    {}    {}    {}    \
                         {}    {}    {}    \
                         {}    {}    \
                         {}    {}    {}\n",
                        r.id, r.user_tag, r.work, r.start_time, r.end_time,
                        r.perf.global, r.perf.window, r.perf.instant,
                        r.start_energy, r.end_energy,
                        r.pwr.global, r.pwr.window, r.pwr.instant).as_bytes())
    }

    /// Rust-only function that logs the buffer (up to buffer_index) to a file.
    pub fn log_to_buffer_index(&mut self) -> io::Result<()> {
        match self.log {
            Some(ref mut l) => {
                for i in 0..self.hb.ws.buffer_index {
                    match HeartbeatPow::write_log(self.hbr.get(i as usize).unwrap(), l) {
                        Ok(_) => (),
                        Err(e) => return Err(e),
                    }
                }
                l.flush()
            }
            None => Ok(())
        }
    }
}

#[cfg(test)]
mod test {
    use super::HeartbeatPow;
    use std::fs::File;

    #[test]
    fn test_simple() {
        const TIME_INC: u64 = 1000000000;
        const ENERGY_INC: u64 = 1000000;
        let mut hb = HeartbeatPow::new(5, None, None).unwrap();
        let mut start_time: u64 = 0;
        let mut end_time: u64 = TIME_INC;
        let mut start_energy: u64 = 0;
        let mut end_energy: u64 = ENERGY_INC;
        for tag in 0..10 {
            hb.heartbeat(tag, 1, start_time, end_time, start_energy, end_energy);
            start_time = end_time;
            end_time += TIME_INC;
            start_energy = end_energy;
            end_energy += ENERGY_INC;
        }
    }

    #[test]
    fn test_file() {
        let mut hb = HeartbeatPow::new(5, None, Some(File::create("foo.log").unwrap())).unwrap();
        hb.heartbeat(0, 1, 0, 1, 0, 0);
        hb.log_to_buffer_index().unwrap();
    }
}
<|endoftext|>


<commit_before>use super::internal::*;
use super::*;
use std::usize;
use std::cmp;

/// `Interleave` is an iterator that interleaves elements of iterators
/// `i` and `j` in one continuous iterator. This struct is created by
/// the [`interleave()`] method on [`IndexedParallelIterator`]
///
/// [`interleave()`]: trait.IndexedParallelIterator.html#method.interleave
/// [`IndexedParallelIterator`]: trait.IndexedParallelIterator.html
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct Interleave<I, J>
    where I: IndexedParallelIterator,
          J: IndexedParallelIterator<Item = I::Item>
{
    i: I,
    j: J,
}

/// Create a new `Interleave` iterator
///
/// NB: a free fn because it is NOT part of the end-user API.
pub fn new<I, J>(i: I, j: J) -> Interleave<I, J>
    where I: IndexedParallelIterator,
          J: IndexedParallelIterator<Item = I::Item>
{
    Interleave { i: i, j: j }
}

impl<I, J> ParallelIterator for Interleave<I, J>
    where I: IndexedParallelIterator,
          J: IndexedParallelIterator<Item = I::Item>
{
    type Item = I::Item;

    fn drive_unindexed<C>(self, consumer: C) -> C::Result
        where C: Consumer<I::Item>
    {
        bridge(self, consumer)
    }

    fn opt_len(&mut self) -> Option<usize> {
        Some(self.len())
    }
}

impl<I, J> IndexedParallelIterator for Interleave<I, J>
    where I: IndexedParallelIterator,
          J: IndexedParallelIterator<Item = I::Item>,
{
    fn drive<C>(self, consumer: C) -> C::Result
        where C: Consumer<Self::Item>
    {
        bridge(self, consumer)
    }

    fn len(&mut self) -> usize {
        self.i.len() + self.j.len()
    }

    fn with_producer<CB>(mut self, callback: CB) -> CB::Output
        where CB: ProducerCallback<Self::Item>
    {
        let (i_len, j_len) = (self.i.len(), self.j.len());
        return self.i.with_producer(CallbackI {
            callback: callback,
            i_len: i_len,
            j_len: j_len,
            flag: false,
            j: self.j
        });

        struct CallbackI<CB, J> {
            callback: CB,
            i_len: usize,
            j_len: usize,
            flag: bool,
            j: J
        }

        impl<CB, J> ProducerCallback<J::Item> for CallbackI<CB, J>
            where J: IndexedParallelIterator,
                  CB: ProducerCallback<J::Item>
        {
            type Output = CB::Output;

            fn callback<I>(self, i_producer: I) -> Self::Output
                where I: Producer<Item = J::Item>
            {
                self.j.with_producer(CallbackJ {
                    i_producer: i_producer,
                    i_len: self.i_len,
                    j_len: self.j_len,
                    flag: self.flag,
                    callback: self.callback
                })
            }
        }

        struct CallbackJ<CB, I> {
            callback: CB,
            i_len: usize,
            j_len: usize,
            flag: bool,
            i_producer: I
        }

        impl<CB, I> ProducerCallback<I::Item> for CallbackJ<CB, I>
            where I: Producer,
                  CB: ProducerCallback<I::Item>
        {
            type Output = CB::Output;

            fn callback<J>(self, j_producer: J) -> Self::Output
                where J: Producer<Item = I::Item>
            {
                let producer = InterleaveProducer::new(self.i_producer, j_producer, self.i_len, self.j_len, self.flag);
                self.callback.callback(producer)
            }
        }
    }
}

pub struct InterleaveProducer<I, J>
    where I: Producer,
          J: Producer<Item = I::Item>
{
    i: I,
    j: J,
    i_len: usize,
    j_len: usize,
    flag: bool,
}

impl<I, J> InterleaveProducer<I, J>
    where I: Producer,
          J: Producer<Item = I::Item>
{
    fn new(i: I, j: J, i_len: usize, j_len: usize, flag: bool) -> InterleaveProducer<I, J> {
        InterleaveProducer { i: i, j: j, i_len: i_len, j_len: j_len, flag: flag }
    }
}

impl<I, J> Producer for InterleaveProducer<I, J>
    where I: Producer,
          J: Producer<Item = I::Item>
{
    type Item = I::Item;
    type IntoIter = InterleaveSeq<I::IntoIter, J::IntoIter>;

    fn into_iter(self) -> Self::IntoIter {
        InterleaveSeq {
            i: self.i.into_iter(),
            j: self.j.into_iter(),
            flag: self.flag,
        }
    }

    fn min_len(&self) -> usize {
        cmp::max(self.i.min_len(), self.j.min_len())
    }

    fn max_len(&self) -> usize {
        cmp::min(self.i.max_len(), self.j.max_len())
    }

    /// We know 0 < index <= self.i_len + self.j_len
    ///
    /// Find a, b satisfying:
    ///
    ///  (1) 0 < a <= self.i_len
    ///  (2) 0 < b <= self.j_len
    ///  (3) a + b == index
    ///
    /// For even splits, set a = b = index/2.
    /// For odd splits, set a = (index/2)+1, b = index/2, if `i`
    /// should yield the next element, otherwise, if `j` should yield
    /// the next element, set a = index/2 and b = (index/2)+1
    fn split_at(self, index: usize) -> (Self, Self) {
        let even = index%2 == 0;
        let idx = index >> 1;

        let odd_offset = |flag| if flag { 0 } else { 1 };

        // desired split
        let (i_idx, j_idx) = (idx + odd_offset(even || self.flag),
                              idx + odd_offset(even || !self.flag));

        let (i_split, j_split) = if self.i_len >= i_idx && self.j_len >= j_idx {
            (i_idx, j_idx)
        } else if self.i_len >= i_idx {
            // j too short
            (index - self.j_len, self.j_len)
        } else {
            // i too short
            (self.i_len, index - self.i_len)
        };

        let trailing_flag = even == self.flag;
        let (i_left, i_right) = self.i.split_at(i_split);
        let (j_left, j_right) = self.j.split_at(j_split);

        (InterleaveProducer::new(
            i_left,
            j_left,
            i_split,
            j_split,
            self.flag
        ), InterleaveProducer::new(
            i_right,
            j_right,
            self.i_len - i_split,
            self.j_len - j_split,
            trailing_flag
        ))
    }
}

/// Wrapper for Interleave to implement DoubleEndedIterator and
/// ExactSizeIterator.
pub struct InterleaveSeq<I, J> {
    i: I,
    j: J,

    /// Flag to control which iterator should provide the next
    /// element. If `false` then `i` produces the next element, when
    /// `true` then the next element should be from `j`.
    flag: bool
}

/// Iterator implementation for InterleaveSeq. This implementation is
/// taken more or less verbatim from itertools. It is replicated here
/// (instead of calling itertools directly), because we also need to
/// implement `DoubledEndedIterator` and `ExactSizeIterator`.
impl<I, J> Iterator for InterleaveSeq<I, J>
    where I: Iterator + ExactSizeIterator,
          J: Iterator<Item = I::Item> + ExactSizeIterator
{
    type Item = I::Item;

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        self.flag = !self.flag;
        if self.flag {
            match self.i.next() {
                None => self.j.next(),
                r => r
            }
        } else {
            match self.j.next() {
                None => self.i.next(),
                r => r
            }
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let (ih, jh) = (self.i.size_hint(), self.j.size_hint());
        let min = ih.0.checked_add(jh.0).unwrap_or(usize::MAX);
        let max = match (ih.1, jh.1) {
            (Some(x), Some(y)) => x.checked_add(y),
            _=> None
        };
        (min, max)
    }
}

// The implementation for DoubleEndedIterator requires
// ExactSizeIterator to provide `next_back()`. The last element will
// come from the iterator that runs out last (ie has the most elements
// in it). If the iterators have the same number of elements, then the
// last iterator will provide the last element.
impl<I, J> DoubleEndedIterator for InterleaveSeq<I, J>
    where I: DoubleEndedIterator + ExactSizeIterator,
          J: DoubleEndedIterator<Item = I::Item> + ExactSizeIterator<Item = I::Item>
{
    #[inline]
    fn next_back(&mut self) -> Option<I::Item> {
        if self.i.len() == self.j.len() {
            if self.flag {
                self.i.next_back()
            } else {
                self.j.next_back()
            }
        } else if self.i.len() < self.j.len() {
            self.j.next_back()
        } else {
            self.i.next_back()
        }
    }
}

impl<I, J> ExactSizeIterator for InterleaveSeq<I, J>
    where I: ExactSizeIterator,
          J: ExactSizeIterator<Item = I::Item>
{
    #[inline]
    fn len(&self) -> usize {
        self.i.len() + self.j.len()
    }
}
<commit_msg>interleave: More review changes<commit_after>use super::internal::*;
use super::*;
use std::usize;
use std::cmp;

/// `Interleave` is an iterator that interleaves elements of iterators
/// `i` and `j` in one continuous iterator. This struct is created by
/// the [`interleave()`] method on [`IndexedParallelIterator`]
///
/// [`interleave()`]: trait.IndexedParallelIterator.html#method.interleave
/// [`IndexedParallelIterator`]: trait.IndexedParallelIterator.html
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct Interleave<I, J>
    where I: IndexedParallelIterator,
          J: IndexedParallelIterator<Item = I::Item>
{
    i: I,
    j: J,
}

/// Create a new `Interleave` iterator
///
/// NB: a free fn because it is NOT part of the end-user API.
pub fn new<I, J>(i: I, j: J) -> Interleave<I, J>
    where I: IndexedParallelIterator,
          J: IndexedParallelIterator<Item = I::Item>
{
    Interleave { i: i, j: j }
}

impl<I, J> ParallelIterator for Interleave<I, J>
    where I: IndexedParallelIterator,
          J: IndexedParallelIterator<Item = I::Item>
{
    type Item = I::Item;

    fn drive_unindexed<C>(self, consumer: C) -> C::Result
        where C: Consumer<I::Item>
    {
        bridge(self, consumer)
    }

    fn opt_len(&mut self) -> Option<usize> {
        Some(self.len())
    }
}

impl<I, J> IndexedParallelIterator for Interleave<I, J>
    where I: IndexedParallelIterator,
          J: IndexedParallelIterator<Item = I::Item>,
{
    fn drive<C>(self, consumer: C) -> C::Result
        where C: Consumer<Self::Item>
    {
        bridge(self, consumer)
    }

    fn len(&mut self) -> usize {
        self.i.len() + self.j.len()
    }

    fn with_producer<CB>(mut self, callback: CB) -> CB::Output
        where CB: ProducerCallback<Self::Item>
    {
        let (i_len, j_len) = (self.i.len(), self.j.len());
        return self.i.with_producer(CallbackI {
            callback: callback,
            i_len: i_len,
            j_len: j_len,
            flag: false,
            j: self.j
        });

        struct CallbackI<CB, J> {
            callback: CB,
            i_len: usize,
            j_len: usize,
            flag: bool,
            j: J
        }

        impl<CB, J> ProducerCallback<J::Item> for CallbackI<CB, J>
            where J: IndexedParallelIterator,
                  CB: ProducerCallback<J::Item>
        {
            type Output = CB::Output;

            fn callback<I>(self, i_producer: I) -> Self::Output
                where I: Producer<Item = J::Item>
            {
                self.j.with_producer(CallbackJ {
                    i_producer: i_producer,
                    i_len: self.i_len,
                    j_len: self.j_len,
                    flag: self.flag,
                    callback: self.callback
                })
            }
        }

        struct CallbackJ<CB, I> {
            callback: CB,
            i_len: usize,
            j_len: usize,
            flag: bool,
            i_producer: I
        }

        impl<CB, I> ProducerCallback<I::Item> for CallbackJ<CB, I>
            where I: Producer,
                  CB: ProducerCallback<I::Item>
        {
            type Output = CB::Output;

            fn callback<J>(self, j_producer: J) -> Self::Output
                where J: Producer<Item = I::Item>
            {
                let producer = InterleaveProducer::new(self.i_producer, j_producer, self.i_len, self.j_len, self.flag);
                self.callback.callback(producer)
            }
        }
    }
}

pub struct InterleaveProducer<I, J>
    where I: Producer,
          J: Producer<Item = I::Item>
{
    i: I,
    j: J,
    i_len: usize,
    j_len: usize,
    flag: bool,
}

impl<I, J> InterleaveProducer<I, J>
    where I: Producer,
          J: Producer<Item = I::Item>
{
    fn new(i: I, j: J, i_len: usize, j_len: usize, flag: bool) -> InterleaveProducer<I, J> {
        InterleaveProducer { i: i, j: j, i_len: i_len, j_len: j_len, flag: flag }
    }
}

impl<I, J> Producer for InterleaveProducer<I, J>
    where I: Producer,
          J: Producer<Item = I::Item>
{
    type Item = I::Item;
    type IntoIter = InterleaveSeq<I::IntoIter, J::IntoIter>;

    fn into_iter(self) -> Self::IntoIter {
        InterleaveSeq {
            i: self.i.into_iter(),
            j: self.j.into_iter(),
            flag: self.flag,
        }
    }

    fn min_len(&self) -> usize {
        cmp::max(self.i.min_len(), self.j.min_len())
    }

    fn max_len(&self) -> usize {
        cmp::min(self.i.max_len(), self.j.max_len())
    }

    /// We know 0 < index <= self.i_len + self.j_len
    ///
    /// Find a, b satisfying:
    ///
    ///  (1) 0 < a <= self.i_len
    ///  (2) 0 < b <= self.j_len
    ///  (3) a + b == index
    ///
    /// For even splits, set a = b = index/2.
    /// For odd splits, set a = (index/2)+1, b = index/2, if `i`
    /// should yield the next element, otherwise, if `j` should yield
    /// the next element, set a = index/2 and b = (index/2)+1
    fn split_at(self, index: usize) -> (Self, Self) {
        let even = index%2 == 0;
        let idx = index >> 1;

        let odd_offset = |flag| if flag { 0 } else { 1 };

        // desired split
        let (i_idx, j_idx) = (idx + odd_offset(even || self.flag),
                              idx + odd_offset(even || !self.flag));

        let (i_split, j_split) = if self.i_len >= i_idx && self.j_len >= j_idx {
            (i_idx, j_idx)
        } else if self.i_len >= i_idx {
            // j too short
            (index - self.j_len, self.j_len)
        } else {
            // i too short
            (self.i_len, index - self.i_len)
        };

        let trailing_flag = even == self.flag;
        let (i_left, i_right) = self.i.split_at(i_split);
        let (j_left, j_right) = self.j.split_at(j_split);

        (InterleaveProducer::new(
            i_left,
            j_left,
            i_split,
            j_split,
            self.flag
        ), InterleaveProducer::new(
            i_right,
            j_right,
            self.i_len - i_split,
            self.j_len - j_split,
            trailing_flag
        ))
    }
}

/// Wrapper for Interleave to implement DoubleEndedIterator and
/// ExactSizeIterator.
pub struct InterleaveSeq<I, J> {
    i: I,
    j: J,

    /// Flag to control which iterator should provide the next
    /// element. If `false` then `i` produces the next element, when
    /// `true` then the next element should be from `j`.
    flag: bool
}

/// Iterator implementation for InterleaveSeq. This implementation is
/// taken more or less verbatim from itertools. It is replicated here
/// (instead of calling itertools directly), because we also need to
/// implement `DoubledEndedIterator` and `ExactSizeIterator`.
impl<I, J> Iterator for InterleaveSeq<I, J>
    where I: Iterator,
          J: Iterator<Item = I::Item>
{
    type Item = I::Item;

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        self.flag = !self.flag;
        if self.flag {
            match self.i.next() {
                None => self.j.next(),
                r => r
            }
        } else {
            match self.j.next() {
                None => self.i.next(),
                r => r
            }
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let (ih, jh) = (self.i.size_hint(), self.j.size_hint());
        let min = ih.0.saturating_add(jh.0);
        let max = match (ih.1, jh.1) {
            (Some(x), Some(y)) => x.checked_add(y),
            _=> None
        };
        (min, max)
    }
}

// The implementation for DoubleEndedIterator requires
// ExactSizeIterator to provide `next_back()`. The last element will
// come from the iterator that runs out last (ie has the most elements
// in it). If the iterators have the same number of elements, then the
// last iterator will provide the last element.
impl<I, J> DoubleEndedIterator for InterleaveSeq<I, J>
    where I: DoubleEndedIterator + ExactSizeIterator,
          J: DoubleEndedIterator<Item = I::Item> + ExactSizeIterator<Item = I::Item>
{
    #[inline]
    fn next_back(&mut self) -> Option<I::Item> {
        if self.i.len() == self.j.len() {
            if self.flag {
                self.i.next_back()
            } else {
                self.j.next_back()
            }
        } else if self.i.len() < self.j.len() {
            self.j.next_back()
        } else {
            self.i.next_back()
        }
    }
}

impl<I, J> ExactSizeIterator for InterleaveSeq<I, J>
    where I: ExactSizeIterator,
          J: ExactSizeIterator<Item = I::Item>
{
    #[inline]
    fn len(&self) -> usize {
        self.i.len() + self.j.len()
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]

use scaii_defs::protos;
use scaii_defs::{Module, Recorder};
use scaii_defs::protos::{MultiMessage, RecorderStep, ScaiiPacket, RecorderConfig};
use scaii_defs::protos::scaii_packet::SpecificMsg;
use std::error::Error;
use std::env;
use std::fmt;
use std::path::PathBuf;
use std::fs;

use bincode::{serialize, Infinite};
use std::io::prelude::*;
use std::fs::File;
use prost::Message;

#[cfg(test)]
mod test;

#[derive(Debug)]
struct RecorderError {
    details: String
}

impl RecorderError {
    fn new(msg: &str) -> RecorderError {
        RecorderError{details: msg.to_string()}
    }
}

impl fmt::Display for RecorderError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,"{}",self.details)
    }
}

impl Error for RecorderError {
    fn description(&self) -> &str {
        &self.details
    }
}

//
// wrapper structs for serialized proto messages
//

#[derive(Clone,Serialize, Deserialize, PartialEq, Debug)]
pub struct SerializedProtosSerializationResponse {
    pub data: Vec<u8>,
}
#[derive(Clone,Serialize, Deserialize, PartialEq, Debug)]
pub struct SerializedProtosAction {
    pub data: Vec<u8>,
}
#[derive(Clone,Serialize, Deserialize, PartialEq, Debug)]
pub struct SerializedProtosScaiiPacket {
    pub data: Vec<u8>,
}
#[derive(Clone,Serialize, Deserialize, PartialEq, Debug)]
pub struct SerializedProtosEndpoint {
    pub data: Vec<u8>,
}

//
//  structs supporting ReplayAction
//

#[derive(Clone,Serialize, Deserialize, PartialEq, Debug)]
pub enum GameAction {
     DecisionPoint(SerializedProtosAction),
     Step,
}

#[derive(Clone,Serialize, Deserialize, PartialEq, Debug)]
pub enum ReplayAction {
    Header(ReplayHeader),
    Delta(GameAction),
    Keyframe(SerializationInfo,GameAction),
}

#[derive(Clone,Serialize, Deserialize, PartialEq, Debug)]
pub struct SerializationInfo {
    pub source: SerializedProtosEndpoint,
    pub data: SerializedProtosSerializationResponse,
}

#[derive(Clone,Serialize, Deserialize, PartialEq, Debug)]
pub struct ReplayHeader {
    pub configs: SerializedProtosScaiiPacket,
}

//
// RecorderManager manages recording
//

pub struct RecorderManager {
    staged_ser_info: Option<SerializationInfo>,
    file_path: Option<PathBuf>,
    replay: Vec<ReplayAction>,
    is_recording: bool,
    writable_file: Option<File>
}
impl RecorderManager  {
    pub fn new() -> Self {
        RecorderManager {
            staged_ser_info: None,
            file_path: None,
            replay : Vec::new(),
            is_recording: false,
            writable_file: None,
        }
    }

    fn init(&mut self) -> Result<(), Box<Error>>{
        let replay_dir_path_buf = get_default_replay_dir()?;
        ensure_dir_exists(&replay_dir_path_buf)?;
        let path_buf = get_default_replay_file_path()?;
        self.file_path = Some(path_buf);
        Ok(())
    }

    fn get_serialized_protos_action(&mut self, action: &protos::Action) -> Result<SerializedProtosAction, Box<Error>> {
        let mut action_data: Vec<u8> = Vec::new();
        action.encode(&mut action_data)?;
        Ok(SerializedProtosAction {
            data: action_data
        })
    }

    fn get_serialized_protos_serialization_response(&mut self, ser_resp: &protos::SerializationResponse) -> Result<SerializedProtosSerializationResponse, Box<Error>> {
        let mut ser_resp_data: Vec<u8> = Vec::new();
        ser_resp.encode(&mut ser_resp_data)?;
        Ok(SerializedProtosSerializationResponse { data: ser_resp_data })
    }

    fn get_serialized_protos_endpoint(&mut self, endpoint: &protos::Endpoint) -> Result<SerializedProtosEndpoint, Box<Error>> {
        let mut endpoint_data: Vec<u8> = Vec::new();
        endpoint.encode(&mut endpoint_data)?;
        Ok(SerializedProtosEndpoint { data: endpoint_data })
    }

    fn get_serialized_protos_scaii_packet(&mut self, pkt: &protos::ScaiiPacket) -> Result<SerializedProtosScaiiPacket, Box<Error>> {
        let mut pkt_data: Vec<u8> = Vec::new();
        pkt.encode(&mut pkt_data)?;
        Ok(SerializedProtosScaiiPacket { data: pkt_data })
    }

    fn handle_pkt(&mut self, pkt: &ScaiiPacket) -> Result<(), Box<Error>>{
        let src_endpoint = &pkt.src;
        let ser_protos_src_endpoint = self.get_serialized_protos_endpoint(src_endpoint)?;
        let specific_msg = &pkt.specific_msg;
        match *specific_msg {
            Some(SpecificMsg::RecorderConfig(RecorderConfig{pkts: _, overwrite: ref ow, filepath: ref path})) => {
                self.is_recording = true;
                println!("  overwrite is set to {} , filepath is set to {:?} ", ow, path);
                let ser_protos_scaii_pkt = self.get_serialized_protos_scaii_packet(pkt)?;
                let replay_header = ReplayHeader { configs: ser_protos_scaii_pkt,};
                let replay_action = ReplayAction::Header(replay_header);
                self.start_recording(path, ow, &replay_action)?;
            },
            Some(SpecificMsg::SerResp(ref ser_resp)) => {
                if self.is_recording {
                    let ser_protos_ser_resp = self.get_serialized_protos_serialization_response(ser_resp)?;
                    let ser_info = SerializationInfo { source: ser_protos_src_endpoint, data: ser_protos_ser_resp };
                    if let Some(SerializationInfo { .. }) = self.staged_ser_info {
                        return Err(Box::new(RecorderError::new("Received consecutive SerializationInfo packets - expected RecorderStep in between.")));
                    }
                    self.staged_ser_info = Some(ser_info);
                }
            },
            Some(SpecificMsg::RecorderStep(ref rec_step)) => {
                if self.is_recording {
                    if let Some(SerializationInfo{ .. }) = self.staged_ser_info {
                        self.save_keyframe(rec_step)?;
                    }
                    else {
                        let game_action = self.get_game_action_for_protos_action(rec_step)?;
                        let replay_action = ReplayAction::Delta(game_action);
                        self.persist_replay_action(&replay_action)?;
                    }
                }
            },
            Some(SpecificMsg::GameComplete(_)) => {
                self.stop_recording();
            },
            _ => {},
        }
        Ok(())
    }

    fn stop_recording(&mut self) {
        self.writable_file = None;
    }

    fn get_one_up_path(&mut self, filepath : &String) -> String {
        filepath.clone()
    }
    fn start_recording(&mut self, filepath_option : &Option<String>, overwrite: &bool, header_replay_action : &ReplayAction) -> Result<(), Box<Error>> {
        match filepath_option {
            &None => {},
            &Some(ref path) => {
                if *overwrite {
                    let pathbuf_option : Option<PathBuf> = Some(PathBuf::from(path));
                    self.file_path = pathbuf_option;
                }
                else {
                    let pathbuf_option : Option<PathBuf> = Some(PathBuf::from(self.get_one_up_path(path)));
                    self.file_path = pathbuf_option;
                }
            }
        }
        if self.file_path == None {
            return Err(Box::new(RecorderError::new("RecorderManager.file_path not specified prior to start of recording.")));
        }
        let path = self.file_path.clone().unwrap();
        let f = File::create(&path).expect("could not write to replay file");
        self.writable_file = Some(f);
        self.persist_replay_action(header_replay_action)?;
        Ok(())
    }

    fn persist_replay_action(&mut self, replay_action: &ReplayAction) -> Result<(), Box<Error>>{
        println!("persisting replayAction {:?}", replay_action);
        match self.writable_file {
            None => {
                return Err(Box::new(RecorderError::new("RecorderManager.writable_file not open for recording replay action.")));
            }
            Some(ref mut file) => {
                let encoded: Vec<u8> = serialize(replay_action, Infinite).unwrap();
                let data_size = encoded.len();
                let write_result = file.write(&encoded);
                if write_result.unwrap() != data_size {
                    return Err(Box::new(RecorderError::new("could not write entire replay_action to replay file.")));
                }
            }
        }
        Ok(())
    }

    fn get_game_action_for_protos_action(&mut self, rec_step: &RecorderStep) -> Result<GameAction, Box<Error>> {
        if rec_step.is_decision_point {
            if rec_step.action == None {
                    return Err(Box::new(RecorderError::new("Malformed RecordStep: no action present but is_decision_point was true.")));
            }
            let serialized_protos_action = self.get_serialized_protos_action(&rec_step.action.clone().unwrap())?;
            Ok(GameAction::DecisionPoint(serialized_protos_action))
        }
        else {
            Ok(GameAction::Step)
        }
    }

    fn save_keyframe(&mut self, rec_step: &RecorderStep) ->  Result<(), Box<Error>> {
        if rec_step.action == None {
            return Err(Box::new(RecorderError::new("Cannot create keyFrame to store - action missing.")));
        }
        let game_action = self.get_game_action_for_protos_action(rec_step)?;
        let ser_info = self.staged_ser_info.clone();
        if ser_info == None{
            return Err(Box::new(RecorderError::new("Cannot create keyFrame to store - ser_info missing.")));
        }
        let replay_action = ReplayAction::Keyframe(ser_info.clone().unwrap(), game_action);
        self.persist_replay_action(&replay_action)?;
        self.staged_ser_info = None;
        Ok(())
    }
}

impl Module for RecorderManager  {
    fn process_msg(&mut self, msg: &ScaiiPacket) -> Result<(), Box<Error>>{
        println!("recorderManager handling packet");
        self.handle_pkt(msg)
    }

    /// return empty messages
    fn get_messages(&mut self) -> MultiMessage{
        let pkts: Vec<ScaiiPacket> = Vec::new();
        MultiMessage { packets: pkts }
    }
}

impl Recorder for RecorderManager {}

pub fn get_default_replay_file_path() -> Result<PathBuf, Box<Error>> {
    let mut replay_dir_path_buf = get_default_replay_dir()?;
    replay_dir_path_buf.push("replay_data.sky");
    Ok(replay_dir_path_buf)
}

pub fn get_default_replay_dir() -> Result<PathBuf, Box<Error>> {
    let mut dir = get_scaii_root()?;
    dir.push("core");
    dir.push("replay_data");
    ensure_dir_exists(&dir)?;
    Ok(dir)
}

fn ensure_dir_exists(path_buf: &PathBuf) -> Result<(), Box<Error>> {
    if !path_buf.as_path().exists() {
        fs::create_dir_all(path_buf.as_path())?;
    }
    Ok(())
}
pub fn get_scaii_root() -> Result<PathBuf, Box<Error>> {
    //look upwardfrom current dir until find valid parent.
    let current_dir = env::current_dir()?;
    let mut candidate_dir: PathBuf = current_dir.clone();
    let mut seeking = true;
    while seeking {
        let is_dir_scaii_root = is_dir_scaii_root(&candidate_dir);
        if is_dir_scaii_root {
            seeking = false;
        } else {
            let candidate_clone = candidate_dir.clone();
            let parent_search_result = candidate_clone.parent();
            match parent_search_result {
                Some(parent_path) => {
                    candidate_dir = parent_path.clone().to_path_buf();
                }
                None => {
                    return Err(Box::new(RecorderError::new(&format!(
                        "cannot find scaii_root above current directory {:?}",
                        current_dir
                    ))));
                }
            }
        }
    }
    Ok(candidate_dir)
}

fn is_dir_scaii_root(dir: &PathBuf) -> bool {
    let mut candidate_dir = dir.clone();
    candidate_dir.push("core");
    let core_dir_exists = candidate_dir.exists();

    let mut candidate_dir = dir.clone();
    candidate_dir.push("common_protos");
    let common_protos_dir_exists = candidate_dir.exists();

    core_dir_exists && common_protos_dir_exists
}<commit_msg>default replay path is .scaii/replays/replay.scr<commit_after>#![allow(dead_code)]

use scaii_defs::protos;
use scaii_defs::{Module, Recorder};
use scaii_defs::protos::{MultiMessage, RecorderStep, ScaiiPacket, RecorderConfig};
use scaii_defs::protos::scaii_packet::SpecificMsg;
use std::error::Error;
use std::env;
use std::fmt;
use std::path::PathBuf;
use std::fs;

use bincode::{serialize, Infinite};
use std::io::prelude::*;
use std::fs::File;
use prost::Message;

#[cfg(test)]
mod test;

#[derive(Debug)]
struct RecorderError {
    details: String
}

impl RecorderError {
    fn new(msg: &str) -> RecorderError {
        RecorderError{details: msg.to_string()}
    }
}

impl fmt::Display for RecorderError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,"{}",self.details)
    }
}

impl Error for RecorderError {
    fn description(&self) -> &str {
        &self.details
    }
}

//
// wrapper structs for serialized proto messages
//

#[derive(Clone,Serialize, Deserialize, PartialEq, Debug)]
pub struct SerializedProtosSerializationResponse {
    pub data: Vec<u8>,
}
#[derive(Clone,Serialize, Deserialize, PartialEq, Debug)]
pub struct SerializedProtosAction {
    pub data: Vec<u8>,
}
#[derive(Clone,Serialize, Deserialize, PartialEq, Debug)]
pub struct SerializedProtosScaiiPacket {
    pub data: Vec<u8>,
}
#[derive(Clone,Serialize, Deserialize, PartialEq, Debug)]
pub struct SerializedProtosEndpoint {
    pub data: Vec<u8>,
}

//
//  structs supporting ReplayAction
//

#[derive(Clone,Serialize, Deserialize, PartialEq, Debug)]
pub enum GameAction {
     DecisionPoint(SerializedProtosAction),
     Step,
}

#[derive(Clone,Serialize, Deserialize, PartialEq, Debug)]
pub enum ReplayAction {
    Header(ReplayHeader),
    Delta(GameAction),
    Keyframe(SerializationInfo,GameAction),
}

#[derive(Clone,Serialize, Deserialize, PartialEq, Debug)]
pub struct SerializationInfo {
    pub source: SerializedProtosEndpoint,
    pub data: SerializedProtosSerializationResponse,
}

#[derive(Clone,Serialize, Deserialize, PartialEq, Debug)]
pub struct ReplayHeader {
    pub configs: SerializedProtosScaiiPacket,
}

//
// RecorderManager manages recording
//

pub struct RecorderManager {
    staged_ser_info: Option<SerializationInfo>,
    file_path: Option<PathBuf>,
    replay: Vec<ReplayAction>,
    is_recording: bool,
    writable_file: Option<File>
}
impl RecorderManager  {
    pub fn new() -> Self {
        RecorderManager {
            staged_ser_info: None,
            file_path: None,
            replay : Vec::new(),
            is_recording: false,
            writable_file: None,
        }
    }

    fn init(&mut self) -> Result<(), Box<Error>>{
        let replay_dir_path_buf = get_default_replay_dir()?;
        ensure_dir_exists(&replay_dir_path_buf)?;
        let path_buf = get_default_replay_file_path()?;
        self.file_path = Some(path_buf);
        Ok(())
    }

    fn get_serialized_protos_action(&mut self, action: &protos::Action) -> Result<SerializedProtosAction, Box<Error>> {
        let mut action_data: Vec<u8> = Vec::new();
        action.encode(&mut action_data)?;
        Ok(SerializedProtosAction {
            data: action_data
        })
    }

    fn get_serialized_protos_serialization_response(&mut self, ser_resp: &protos::SerializationResponse) -> Result<SerializedProtosSerializationResponse, Box<Error>> {
        let mut ser_resp_data: Vec<u8> = Vec::new();
        ser_resp.encode(&mut ser_resp_data)?;
        Ok(SerializedProtosSerializationResponse { data: ser_resp_data })
    }

    fn get_serialized_protos_endpoint(&mut self, endpoint: &protos::Endpoint) -> Result<SerializedProtosEndpoint, Box<Error>> {
        let mut endpoint_data: Vec<u8> = Vec::new();
        endpoint.encode(&mut endpoint_data)?;
        Ok(SerializedProtosEndpoint { data: endpoint_data })
    }

    fn get_serialized_protos_scaii_packet(&mut self, pkt: &protos::ScaiiPacket) -> Result<SerializedProtosScaiiPacket, Box<Error>> {
        let mut pkt_data: Vec<u8> = Vec::new();
        pkt.encode(&mut pkt_data)?;
        Ok(SerializedProtosScaiiPacket { data: pkt_data })
    }

    fn handle_pkt(&mut self, pkt: &ScaiiPacket) -> Result<(), Box<Error>>{
        let src_endpoint = &pkt.src;
        let ser_protos_src_endpoint = self.get_serialized_protos_endpoint(src_endpoint)?;
        let specific_msg = &pkt.specific_msg;
        match *specific_msg {
            Some(SpecificMsg::RecorderConfig(RecorderConfig{pkts: _, overwrite: ref ow, filepath: ref path})) => {
                self.is_recording = true;
                println!("  overwrite is set to {} , filepath is set to {:?} ", ow, path);
                let ser_protos_scaii_pkt = self.get_serialized_protos_scaii_packet(pkt)?;
                let replay_header = ReplayHeader { configs: ser_protos_scaii_pkt,};
                let replay_action = ReplayAction::Header(replay_header);
                self.start_recording(path, ow, &replay_action)?;
            },
            Some(SpecificMsg::SerResp(ref ser_resp)) => {
                if self.is_recording {
                    let ser_protos_ser_resp = self.get_serialized_protos_serialization_response(ser_resp)?;
                    let ser_info = SerializationInfo { source: ser_protos_src_endpoint, data: ser_protos_ser_resp };
                    if let Some(SerializationInfo { .. }) = self.staged_ser_info {
                        return Err(Box::new(RecorderError::new("Received consecutive SerializationInfo packets - expected RecorderStep in between.")));
                    }
                    self.staged_ser_info = Some(ser_info);
                }
            },
            Some(SpecificMsg::RecorderStep(ref rec_step)) => {
                if self.is_recording {
                    if let Some(SerializationInfo{ .. }) = self.staged_ser_info {
                        self.save_keyframe(rec_step)?;
                    }
                    else {
                        let game_action = self.get_game_action_for_protos_action(rec_step)?;
                        let replay_action = ReplayAction::Delta(game_action);
                        self.persist_replay_action(&replay_action)?;
                    }
                }
            },
            Some(SpecificMsg::GameComplete(_)) => {
                self.stop_recording();
            },
            _ => {},
        }
        Ok(())
    }

    fn stop_recording(&mut self) {
        self.writable_file = None;
    }

    fn get_one_up_path(&mut self, filepath : &String) -> String {
        filepath.clone()
    }
    fn start_recording(&mut self, filepath_option : &Option<String>, overwrite: &bool, header_replay_action : &ReplayAction) -> Result<(), Box<Error>> {
        match filepath_option {
            &None => {},
            &Some(ref path) => {
                if *overwrite {
                    let pathbuf_option : Option<PathBuf> = Some(PathBuf::from(path));
                    self.file_path = pathbuf_option;
                }
                else {
                    let pathbuf_option : Option<PathBuf> = Some(PathBuf::from(self.get_one_up_path(path)));
                    self.file_path = pathbuf_option;
                }
            }
        }
        if self.file_path == None {
            return Err(Box::new(RecorderError::new("RecorderManager.file_path not specified prior to start of recording.")));
        }
        let path = self.file_path.clone().unwrap();
        let f = File::create(&path).expect("could not write to replay file");
        self.writable_file = Some(f);
        self.persist_replay_action(header_replay_action)?;
        Ok(())
    }

    fn persist_replay_action(&mut self, replay_action: &ReplayAction) -> Result<(), Box<Error>>{
        println!("persisting replayAction {:?}", replay_action);
        match self.writable_file {
            None => {
                return Err(Box::new(RecorderError::new("RecorderManager.writable_file not open for recording replay action.")));
            }
            Some(ref mut file) => {
                let encoded: Vec<u8> = serialize(replay_action, Infinite).unwrap();
                let data_size = encoded.len();
                let write_result = file.write(&encoded);
                if write_result.unwrap() != data_size {
                    return Err(Box::new(RecorderError::new("could not write entire replay_action to replay file.")));
                }
            }
        }
        Ok(())
    }

    fn get_game_action_for_protos_action(&mut self, rec_step: &RecorderStep) -> Result<GameAction, Box<Error>> {
        if rec_step.is_decision_point {
            if rec_step.action == None {
                    return Err(Box::new(RecorderError::new("Malformed RecordStep: no action present but is_decision_point was true.")));
            }
            let serialized_protos_action = self.get_serialized_protos_action(&rec_step.action.clone().unwrap())?;
            Ok(GameAction::DecisionPoint(serialized_protos_action))
        }
        else {
            Ok(GameAction::Step)
        }
    }

    fn save_keyframe(&mut self, rec_step: &RecorderStep) ->  Result<(), Box<Error>> {
        if rec_step.action == None {
            return Err(Box::new(RecorderError::new("Cannot create keyFrame to store - action missing.")));
        }
        let game_action = self.get_game_action_for_protos_action(rec_step)?;
        let ser_info = self.staged_ser_info.clone();
        if ser_info == None{
            return Err(Box::new(RecorderError::new("Cannot create keyFrame to store - ser_info missing.")));
        }
        let replay_action = ReplayAction::Keyframe(ser_info.clone().unwrap(), game_action);
        self.persist_replay_action(&replay_action)?;
        self.staged_ser_info = None;
        Ok(())
    }
}

impl Module for RecorderManager  {
    fn process_msg(&mut self, msg: &ScaiiPacket) -> Result<(), Box<Error>>{
        println!("recorderManager handling packet");
        self.handle_pkt(msg)
    }

    /// return empty messages
    fn get_messages(&mut self) -> MultiMessage{
        let pkts: Vec<ScaiiPacket> = Vec::new();
        MultiMessage { packets: pkts }
    }
}

impl Recorder for RecorderManager {}

pub fn get_default_replay_file_path() -> Result<PathBuf, Box<Error>> {
    let mut replay_dir_path_buf = get_default_replay_dir()?;
    replay_dir_path_buf.push("replay.scr");
    Ok(replay_dir_path_buf)
}

pub fn get_default_replay_dir() -> Result<PathBuf, Box<Error>> {
    let mut dir = get_home_dir()?;
    dir.push(".scaii");
    dir.push("replays");
    ensure_dir_exists(&dir)?;
    Ok(dir)
}

fn get_home_dir() -> Result<PathBuf, Box<Error>> {
    let result : Option<PathBuf> = env::home_dir();
    match result {
        Some(pathbuf) => { Ok(pathbuf) },
        None => {
            Err(Box::new(RecorderError::new("could not determine user's home directory")))
        }
    }
}

fn ensure_dir_exists(path_buf: &PathBuf) -> Result<(), Box<Error>> {
    if !path_buf.as_path().exists() {
        fs::create_dir_all(path_buf.as_path())?;
    }
    Ok(())
}
pub fn get_scaii_root() -> Result<PathBuf, Box<Error>> {
    //look upwardfrom current dir until find valid parent.
    let current_dir = env::current_dir()?;
    let mut candidate_dir: PathBuf = current_dir.clone();
    let mut seeking = true;
    while seeking {
        let is_dir_scaii_root = is_dir_scaii_root(&candidate_dir);
        if is_dir_scaii_root {
            seeking = false;
        } else {
            let candidate_clone = candidate_dir.clone();
            let parent_search_result = candidate_clone.parent();
            match parent_search_result {
                Some(parent_path) => {
                    candidate_dir = parent_path.clone().to_path_buf();
                }
                None => {
                    return Err(Box::new(RecorderError::new(&format!(
                        "cannot find scaii_root above current directory {:?}",
                        current_dir
                    ))));
                }
            }
        }
    }
    Ok(candidate_dir)
}

fn is_dir_scaii_root(dir: &PathBuf) -> bool {
    let mut candidate_dir = dir.clone();
    candidate_dir.push("core");
    let core_dir_exists = candidate_dir.exists();

    let mut candidate_dir = dir.clone();
    candidate_dir.push("common_protos");
    let common_protos_dir_exists = candidate_dir.exists();

    core_dir_exists && common_protos_dir_exists
}<|endoftext|>