<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, CompletionResponse},
    lsp_runtime_error::{LSPRuntimeError, LSPRuntimeResult},
    node_resolution_info::{TypePath, TypePathItem},
    server::LSPState,
};
use common::{PerfLogger, Span};

use graphql_ir::Program;
use graphql_syntax::{
    Argument, Directive, DirectiveLocation, ExecutableDefinition, ExecutableDocument,
    FragmentSpread, InlineFragment, LinkedField, List, OperationDefinition, OperationKind,
    ScalarField, Selection,
};
use interner::StringKey;
use log::info;
use lsp_types::request::{Completion, Request};
use schema::{
    ArgumentDefinitions, Directive as SchemaDirective, Schema, Type, TypeReference, TypeWithFields,
};
use std::{
    collections::HashMap,
    sync::{Arc, RwLock},
};

#[derive(Debug, Copy, Clone)]
pub enum CompletionKind {
    FieldName,
    FragmentSpread,
    DirectiveName { location: DirectiveLocation },
    FieldArgumentName,
}
#[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,
        }
    }
}

fn create_completion_request(
    document: ExecutableDocument,
    position_span: Span,
    project_name: StringKey,
) -> Option<CompletionRequest> {
    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();
            let args = create_arguments_snippets(&field.arguments, schema);
            let insert_text = match (field.type_.inner().is_scalar(), args.is_empty()) {
                (true, true) => None,
                (true, false) => Some(format!("{}({})", name, args.join(", "))),
                (false, true) => Some(format!("{} {{\n\t$1\n}}", name)),
                (false, false) => Some(format!(
                    "{}({}) {{\n\t${}\n}}",
                    name,
                    args.join(", "),
                    args.len() + 1
                )),
            };
            let (insert_text_format, command) = if insert_text.is_some() {
                (
                    Some(lsp_types::InsertTextFormat::Snippet),
                    Some(lsp_types::Command::new(
                        "Suggest".into(),
                        "editor.action.triggerSuggest".into(),
                        None,
                    )),
                )
            } else {
                (None, None)
            };
            CompletionItem {
                label: name,
                kind: None,
                detail: None,
                documentation: None,
                deprecated: None,
                preselect: None,
                sort_text: None,
                filter_text: None,
                insert_text,
                insert_text_format,
                text_edit: None,
                additional_text_edits: None,
                command,
                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
}

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;
    info!("completion_items_for_request: {:?}", kind);
    match kind {
        CompletionKind::FragmentSpread => {
            let leaf_type = request.type_path.resolve_leaf_type(schema)?;
            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 request.type_path.resolve_leaf_type(schema)? {
            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)
        }
        CompletionKind::FieldArgumentName => {
            let field = request.type_path.resolve_current_field(schema)?;
            Some(
                field
                    .arguments
                    .iter()
                    .map(|arg| {
                        CompletionItem::new_simple(
                            arg.name.lookup().into(),
                            schema.get_type_string(&arg.type_),
                        )
                    })
                    .collect(),
            )
        }
    }
}

fn build_request_from_selections(
    selections: &List<Selection>,
    position_span: Span,
    completion_request: &mut CompletionRequest,
) {
    if selections.items.is_empty() {
        completion_request.kind = CompletionKind::FieldName;
        return;
    }
    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,
                        arguments,
                        ..
                    } = node;
                    completion_request
                        .type_path
                        .add_type(TypePathItem::LinkedField { name: name.value });
                    if let Some(arguments) = arguments {
                        if build_request_from_arguments(
                            arguments,
                            position_span,
                            completion_request,
                        ) {
                            return;
                        }
                    }
                    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,
                        arguments,
                        ..
                    } = node;
                    completion_request
                        .type_path
                        .add_type(TypePathItem::ScalarField { name: name.value });
                    if let Some(arguments) = arguments {
                        if build_request_from_arguments(
                            arguments,
                            position_span,
                            completion_request,
                        ) {
                            return;
                        }
                    }
                    build_request_from_directives(
                        directives,
                        DirectiveLocation::Scalar,
                        position_span,
                        completion_request,
                    );
                }
            }
        }
    }
}

fn build_request_from_arguments(
    arguments: &List<Argument>,
    position_span: Span,
    completion_request: &mut CompletionRequest,
) -> bool {
    if arguments.span.contains(position_span) {
        if arguments.items.is_empty() {
            completion_request.kind = CompletionKind::FieldArgumentName;
            return true;
        } else {
            for Argument { name, .. } in &arguments.items {
                if name.span.contains(position_span) {
                    completion_request.kind = CompletionKind::FieldArgumentName;
                    return true;
                }
            }
        }
    }
    false
}

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 args = create_arguments_snippets(&arguments, schema);
        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,
    }
}

fn create_arguments_snippets(arguments: &ArgumentDefinitions, schema: &Schema) -> Vec<String> {
    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;
        }
    }
    args
}

pub(crate) fn on_completion<TPerfLogger: PerfLogger + 'static>(
    state: &mut LSPState<TPerfLogger>,
    params: <Completion as Request>::Params,
) -> LSPRuntimeResult<<Completion as Request>::Result> {
    let (document, position_span, project_name) =
        state.extract_executable_document_from_text(params.text_document_position)?;

    let completion_request = create_completion_request(document, position_span, project_name)
        .ok_or(LSPRuntimeError::ExpectedError)?;

    if let Some(schema) = state.get_schemas().read().unwrap().get(&project_name) {
        let items = completion_items_for_request(
            completion_request,
            schema,
            state.get_source_programs_ref(),
        )
        .unwrap_or_else(Vec::new);
        Ok(Some(CompletionResponse::Array(items)))
    } else {
        Err(LSPRuntimeError::ExpectedError)
    }
}
<commit_msg>Fix auto complete an existing linked field name<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, CompletionResponse},
    lsp_runtime_error::{LSPRuntimeError, LSPRuntimeResult},
    node_resolution_info::{TypePath, TypePathItem},
    server::LSPState,
};
use common::{PerfLogger, Span};

use graphql_ir::Program;
use graphql_syntax::{
    Argument, Directive, DirectiveLocation, ExecutableDefinition, ExecutableDocument,
    FragmentSpread, InlineFragment, LinkedField, List, OperationDefinition, OperationKind,
    ScalarField, Selection,
};
use interner::StringKey;
use log::info;
use lsp_types::request::{Completion, Request};
use schema::{
    ArgumentDefinitions, Directive as SchemaDirective, Schema, Type, TypeReference, TypeWithFields,
};
use std::{
    collections::HashMap,
    sync::{Arc, RwLock},
};

#[derive(Debug, Copy, Clone)]
pub enum CompletionKind {
    FieldName { existing_linked_field: bool },
    FragmentSpread,
    DirectiveName { location: DirectiveLocation },
    FieldArgumentName,
}
#[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 {
                existing_linked_field: false,
            },
            type_path: Default::default(),
            project_name,
        }
    }
}

fn create_completion_request(
    document: ExecutableDocument,
    position_span: Span,
    project_name: StringKey,
) -> Option<CompletionRequest> {
    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,
    existing_linked_field: bool,
) -> Vec<CompletionItem> {
    type_
        .fields()
        .iter()
        .map(|field_id| {
            let field = schema.field(*field_id);
            let name = field.name.to_string();
            let args = create_arguments_snippets(&field.arguments, schema);
            let insert_text = match (
                existing_linked_field || field.type_.inner().is_scalar(),
                args.is_empty(),
            ) {
                (true, true) => None,
                (true, false) => Some(format!("{}({})", name, args.join(", "))),
                (false, true) => Some(format!("{} {{\n\t$1\n}}", name)),
                (false, false) => Some(format!(
                    "{}({}) {{\n\t${}\n}}",
                    name,
                    args.join(", "),
                    args.len() + 1
                )),
            };
            let (insert_text_format, command) = if insert_text.is_some() {
                (
                    Some(lsp_types::InsertTextFormat::Snippet),
                    Some(lsp_types::Command::new(
                        "Suggest".into(),
                        "editor.action.triggerSuggest".into(),
                        None,
                    )),
                )
            } else {
                (None, None)
            };
            CompletionItem {
                label: name,
                kind: None,
                detail: None,
                documentation: None,
                deprecated: None,
                preselect: None,
                sort_text: None,
                filter_text: None,
                insert_text,
                insert_text_format,
                text_edit: None,
                additional_text_edits: None,
                command,
                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
}

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;
    info!("completion_items_for_request: {:?}", kind);
    match kind {
        CompletionKind::FragmentSpread => {
            let leaf_type = request.type_path.resolve_leaf_type(schema)?;
            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 {
            existing_linked_field,
        } => match request.type_path.resolve_leaf_type(schema)? {
            Type::Interface(interface_id) => {
                let interface = schema.interface(interface_id);
                let items =
                    resolve_completion_items_from_fields(interface, schema, existing_linked_field);
                Some(items)
            }
            Type::Object(object_id) => {
                let object = schema.object(object_id);
                let items =
                    resolve_completion_items_from_fields(object, schema, existing_linked_field);
                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)
        }
        CompletionKind::FieldArgumentName => {
            let field = request.type_path.resolve_current_field(schema)?;
            Some(
                field
                    .arguments
                    .iter()
                    .map(|arg| {
                        CompletionItem::new_simple(
                            arg.name.lookup().into(),
                            schema.get_type_string(&arg.type_),
                        )
                    })
                    .collect(),
            )
        }
    }
}

fn build_request_from_selections(
    selections: &List<Selection>,
    position_span: Span,
    completion_request: &mut CompletionRequest,
) {
    if selections.items.is_empty() {
        completion_request.kind = CompletionKind::FieldName {
            existing_linked_field: false,
        };
        return;
    }
    for item in &selections.items {
        if item.span().contains(position_span) {
            match item {
                Selection::LinkedField(node) => {
                    if node.name.span.contains(position_span) {
                        completion_request.kind = CompletionKind::FieldName {
                            existing_linked_field: true,
                        };
                        return;
                    }
                    let LinkedField {
                        name,
                        selections,
                        directives,
                        arguments,
                        ..
                    } = node;
                    completion_request
                        .type_path
                        .add_type(TypePathItem::LinkedField { name: name.value });
                    if let Some(arguments) = arguments {
                        if build_request_from_arguments(
                            arguments,
                            position_span,
                            completion_request,
                        ) {
                            return;
                        }
                    }
                    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,
                        arguments,
                        ..
                    } = node;
                    completion_request
                        .type_path
                        .add_type(TypePathItem::ScalarField { name: name.value });
                    if let Some(arguments) = arguments {
                        if build_request_from_arguments(
                            arguments,
                            position_span,
                            completion_request,
                        ) {
                            return;
                        }
                    }
                    build_request_from_directives(
                        directives,
                        DirectiveLocation::Scalar,
                        position_span,
                        completion_request,
                    );
                }
            }
        }
    }
}

fn build_request_from_arguments(
    arguments: &List<Argument>,
    position_span: Span,
    completion_request: &mut CompletionRequest,
) -> bool {
    if arguments.span.contains(position_span) {
        if arguments.items.is_empty() {
            completion_request.kind = CompletionKind::FieldArgumentName;
            return true;
        } else {
            for Argument { name, .. } in &arguments.items {
                if name.span.contains(position_span) {
                    completion_request.kind = CompletionKind::FieldArgumentName;
                    return true;
                }
            }
        }
    }
    false
}

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 args = create_arguments_snippets(&arguments, schema);
        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,
    }
}

fn create_arguments_snippets(arguments: &ArgumentDefinitions, schema: &Schema) -> Vec<String> {
    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;
        }
    }
    args
}

pub(crate) fn on_completion<TPerfLogger: PerfLogger + 'static>(
    state: &mut LSPState<TPerfLogger>,
    params: <Completion as Request>::Params,
) -> LSPRuntimeResult<<Completion as Request>::Result> {
    let (document, position_span, project_name) =
        state.extract_executable_document_from_text(params.text_document_position)?;

    let completion_request = create_completion_request(document, position_span, project_name)
        .ok_or(LSPRuntimeError::ExpectedError)?;

    if let Some(schema) = state.get_schemas().read().unwrap().get(&project_name) {
        let items = completion_items_for_request(
            completion_request,
            schema,
            state.get_source_programs_ref(),
        )
        .unwrap_or_else(Vec::new);
        Ok(Some(CompletionResponse::Array(items)))
    } else {
        Err(LSPRuntimeError::ExpectedError)
    }
}
<|endoftext|>


<commit_before>use rustc_errors::{Applicability, DiagnosticBuilder};

use rustc_ast::ptr::P;
use rustc_ast::token;
use rustc_ast::tokenstream::{DelimSpan, TokenStream};
use rustc_ast::{self as ast, *};
use rustc_ast_pretty::pprust;
use rustc_expand::base::*;
use rustc_parse::parser::Parser;
use rustc_span::symbol::{sym, Ident, Symbol};
use rustc_span::{Span, DUMMY_SP};

pub fn expand_assert<'cx>(
    cx: &'cx mut ExtCtxt<'_>,
    sp: Span,
    tts: TokenStream,
) -> Box<dyn MacResult + 'cx> {
    let Assert { cond_expr, custom_message } = match parse_assert(cx, sp, tts) {
        Ok(assert) => assert,
        Err(mut err) => {
            err.emit();
            return DummyResult::any(sp);
        }
    };

    let is_2021 = sp.rust_2021();

    // `core::panic` and `std::panic` are different macros, so we use call-site
    // context to pick up whichever is currently in scope.
    let sp = cx.with_call_site_ctxt(sp);

    let panic_call = if let Some(tokens) = custom_message {
        let path = if is_2021 {
            // On edition 2021, we always call `$crate::panic!()`.
            Path {
                span: sp,
                segments: cx
                    .std_path(&[sym::panic])
                    .into_iter()
                    .map(|ident| PathSegment::from_ident(ident))
                    .collect(),
                tokens: None,
            }
        } else {
            // Before edition 2021, we call `panic!()` unqualified,
            // such that it calls either `std::panic!()` or `core::panic!()`.
            Path::from_ident(Ident::new(sym::panic, sp))
        };
        // Pass the custom message to panic!().
        cx.expr(
            sp,
            ExprKind::MacCall(MacCall {
                path,
                args: P(MacArgs::Delimited(
                    DelimSpan::from_single(sp),
                    MacDelimiter::Parenthesis,
                    tokens,
                )),
                prior_type_ascription: None,
            }),
        )
    } else {
        // Pass our own message directly to $crate::panicking::panic(),
        // because it might contain `{` and `}` that should always be
        // passed literally.
        cx.expr_call_global(
            sp,
            cx.std_path(&[sym::panicking, sym::panic]),
            vec![cx.expr_str(
                DUMMY_SP,
                Symbol::intern(&format!(
                    "assertion failed: {}",
                    pprust::expr_to_string(&cond_expr).escape_debug()
                )),
            )],
        )
    };
    let if_expr =
        cx.expr_if(sp, cx.expr(sp, ExprKind::Unary(UnOp::Not, cond_expr)), panic_call, None);
    MacEager::expr(if_expr)
}

struct Assert {
    cond_expr: P<ast::Expr>,
    custom_message: Option<TokenStream>,
}

fn parse_assert<'a>(
    cx: &mut ExtCtxt<'a>,
    sp: Span,
    stream: TokenStream,
) -> Result<Assert, DiagnosticBuilder<'a>> {
    let mut parser = cx.new_parser_from_tts(stream);

    if parser.token == token::Eof {
        let mut err = cx.struct_span_err(sp, "macro requires a boolean expression as an argument");
        err.span_label(sp, "boolean expression required");
        return Err(err);
    }

    let cond_expr = parser.parse_expr()?;

    // Some crates use the `assert!` macro in the following form (note extra semicolon):
    //
    // assert!(
    //     my_function();
    // );
    //
    // Emit an error about semicolon and suggest removing it.
    if parser.token == token::Semi {
        let mut err = cx.struct_span_err(sp, "macro requires an expression as an argument");
        err.span_suggestion(
            parser.token.span,
            "try removing semicolon",
            String::new(),
            Applicability::MaybeIncorrect,
        );
        err.emit();

        parser.bump();
    }

    // Some crates use the `assert!` macro in the following form (note missing comma before
    // message):
    //
    // assert!(true "error message");
    //
    // Emit an error and suggest inserting a comma.
    let custom_message =
        if let token::Literal(token::Lit { kind: token::Str, .. }) = parser.token.kind {
            let mut err = cx.struct_span_err(parser.token.span, "unexpected string literal");
            let comma_span = parser.prev_token.span.shrink_to_hi();
            err.span_suggestion_short(
                comma_span,
                "try adding a comma",
                ", ".to_string(),
                Applicability::MaybeIncorrect,
            );
            err.emit();

            parse_custom_message(&mut parser)
        } else if parser.eat(&token::Comma) {
            parse_custom_message(&mut parser)
        } else {
            None
        };

    if parser.token != token::Eof {
        return parser.unexpected();
    }

    Ok(Assert { cond_expr, custom_message })
}

fn parse_custom_message(parser: &mut Parser<'_>) -> Option<TokenStream> {
    let ts = parser.parse_tokens();
    if !ts.is_empty() { Some(ts) } else { None }
}
<commit_msg>Only call span.rust_2021() when necessary.<commit_after>use rustc_errors::{Applicability, DiagnosticBuilder};

use rustc_ast::ptr::P;
use rustc_ast::token;
use rustc_ast::tokenstream::{DelimSpan, TokenStream};
use rustc_ast::{self as ast, *};
use rustc_ast_pretty::pprust;
use rustc_expand::base::*;
use rustc_parse::parser::Parser;
use rustc_span::symbol::{sym, Ident, Symbol};
use rustc_span::{Span, DUMMY_SP};

pub fn expand_assert<'cx>(
    cx: &'cx mut ExtCtxt<'_>,
    span: Span,
    tts: TokenStream,
) -> Box<dyn MacResult + 'cx> {
    let Assert { cond_expr, custom_message } = match parse_assert(cx, span, tts) {
        Ok(assert) => assert,
        Err(mut err) => {
            err.emit();
            return DummyResult::any(span);
        }
    };

    // `core::panic` and `std::panic` are different macros, so we use call-site
    // context to pick up whichever is currently in scope.
    let sp = cx.with_call_site_ctxt(span);

    let panic_call = if let Some(tokens) = custom_message {
        let path = if span.rust_2021() {
            // On edition 2021, we always call `$crate::panic!()`.
            Path {
                span: sp,
                segments: cx
                    .std_path(&[sym::panic])
                    .into_iter()
                    .map(|ident| PathSegment::from_ident(ident))
                    .collect(),
                tokens: None,
            }
        } else {
            // Before edition 2021, we call `panic!()` unqualified,
            // such that it calls either `std::panic!()` or `core::panic!()`.
            Path::from_ident(Ident::new(sym::panic, sp))
        };
        // Pass the custom message to panic!().
        cx.expr(
            sp,
            ExprKind::MacCall(MacCall {
                path,
                args: P(MacArgs::Delimited(
                    DelimSpan::from_single(sp),
                    MacDelimiter::Parenthesis,
                    tokens,
                )),
                prior_type_ascription: None,
            }),
        )
    } else {
        // Pass our own message directly to $crate::panicking::panic(),
        // because it might contain `{` and `}` that should always be
        // passed literally.
        cx.expr_call_global(
            sp,
            cx.std_path(&[sym::panicking, sym::panic]),
            vec![cx.expr_str(
                DUMMY_SP,
                Symbol::intern(&format!(
                    "assertion failed: {}",
                    pprust::expr_to_string(&cond_expr).escape_debug()
                )),
            )],
        )
    };
    let if_expr =
        cx.expr_if(sp, cx.expr(sp, ExprKind::Unary(UnOp::Not, cond_expr)), panic_call, None);
    MacEager::expr(if_expr)
}

struct Assert {
    cond_expr: P<ast::Expr>,
    custom_message: Option<TokenStream>,
}

fn parse_assert<'a>(
    cx: &mut ExtCtxt<'a>,
    sp: Span,
    stream: TokenStream,
) -> Result<Assert, DiagnosticBuilder<'a>> {
    let mut parser = cx.new_parser_from_tts(stream);

    if parser.token == token::Eof {
        let mut err = cx.struct_span_err(sp, "macro requires a boolean expression as an argument");
        err.span_label(sp, "boolean expression required");
        return Err(err);
    }

    let cond_expr = parser.parse_expr()?;

    // Some crates use the `assert!` macro in the following form (note extra semicolon):
    //
    // assert!(
    //     my_function();
    // );
    //
    // Emit an error about semicolon and suggest removing it.
    if parser.token == token::Semi {
        let mut err = cx.struct_span_err(sp, "macro requires an expression as an argument");
        err.span_suggestion(
            parser.token.span,
            "try removing semicolon",
            String::new(),
            Applicability::MaybeIncorrect,
        );
        err.emit();

        parser.bump();
    }

    // Some crates use the `assert!` macro in the following form (note missing comma before
    // message):
    //
    // assert!(true "error message");
    //
    // Emit an error and suggest inserting a comma.
    let custom_message =
        if let token::Literal(token::Lit { kind: token::Str, .. }) = parser.token.kind {
            let mut err = cx.struct_span_err(parser.token.span, "unexpected string literal");
            let comma_span = parser.prev_token.span.shrink_to_hi();
            err.span_suggestion_short(
                comma_span,
                "try adding a comma",
                ", ".to_string(),
                Applicability::MaybeIncorrect,
            );
            err.emit();

            parse_custom_message(&mut parser)
        } else if parser.eat(&token::Comma) {
            parse_custom_message(&mut parser)
        } else {
            None
        };

    if parser.token != token::Eof {
        return parser.unexpected();
    }

    Ok(Assert { cond_expr, custom_message })
}

fn parse_custom_message(parser: &mut Parser<'_>) -> Option<TokenStream> {
    let ts = parser.parse_tokens();
    if !ts.is_empty() { Some(ts) } else { None }
}
<|endoftext|>


<commit_before>use crate::util::check_builtin_macro_attribute;

use rustc_ast as ast;
use rustc_ast::mut_visit::MutVisitor;
use rustc_ast::tokenstream::CanSynthesizeMissingTokens;
use rustc_ast::visit::Visitor;
use rustc_ast::{mut_visit, visit};
use rustc_ast::{AstLike, Attribute};
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_expand::config::StripUnconfigured;
use rustc_expand::configure;
use rustc_parse::parser::ForceCollect;
use rustc_session::utils::FlattenNonterminals;

use rustc_ast::ptr::P;
use rustc_span::symbol::sym;
use rustc_span::Span;
use smallvec::SmallVec;

crate fn expand(
    ecx: &mut ExtCtxt<'_>,
    _span: Span,
    meta_item: &ast::MetaItem,
    annotatable: Annotatable,
) -> Vec<Annotatable> {
    check_builtin_macro_attribute(ecx, meta_item, sym::cfg_eval);
    vec![cfg_eval(ecx, annotatable)]
}

crate fn cfg_eval(ecx: &ExtCtxt<'_>, annotatable: Annotatable) -> Annotatable {
    CfgEval {
        cfg: &mut StripUnconfigured {
            sess: ecx.sess,
            features: ecx.ecfg.features,
            config_tokens: true,
        },
    }
    .configure_annotatable(annotatable)
}

struct CfgEval<'a, 'b> {
    cfg: &'a mut StripUnconfigured<'b>,
}

fn flat_map_annotatable(vis: &mut impl MutVisitor, annotatable: Annotatable) -> Annotatable {
    // Since the item itself has already been configured by the InvocationCollector,
    // we know that fold result vector will contain exactly one element
    match annotatable {
        Annotatable::Item(item) => Annotatable::Item(vis.flat_map_item(item).pop().unwrap()),
        Annotatable::TraitItem(item) => {
            Annotatable::TraitItem(vis.flat_map_trait_item(item).pop().unwrap())
        }
        Annotatable::ImplItem(item) => {
            Annotatable::ImplItem(vis.flat_map_impl_item(item).pop().unwrap())
        }
        Annotatable::ForeignItem(item) => {
            Annotatable::ForeignItem(vis.flat_map_foreign_item(item).pop().unwrap())
        }
        Annotatable::Stmt(stmt) => {
            Annotatable::Stmt(stmt.map(|stmt| vis.flat_map_stmt(stmt).pop().unwrap()))
        }
        Annotatable::Expr(mut expr) => Annotatable::Expr({
            vis.visit_expr(&mut expr);
            expr
        }),
        Annotatable::Arm(arm) => Annotatable::Arm(vis.flat_map_arm(arm).pop().unwrap()),
        Annotatable::ExprField(field) => {
            Annotatable::ExprField(vis.flat_map_expr_field(field).pop().unwrap())
        }
        Annotatable::PatField(fp) => {
            Annotatable::PatField(vis.flat_map_pat_field(fp).pop().unwrap())
        }
        Annotatable::GenericParam(param) => {
            Annotatable::GenericParam(vis.flat_map_generic_param(param).pop().unwrap())
        }
        Annotatable::Param(param) => Annotatable::Param(vis.flat_map_param(param).pop().unwrap()),
        Annotatable::FieldDef(sf) => {
            Annotatable::FieldDef(vis.flat_map_field_def(sf).pop().unwrap())
        }
        Annotatable::Variant(v) => Annotatable::Variant(vis.flat_map_variant(v).pop().unwrap()),
    }
}

struct CfgFinder {
    has_cfg_or_cfg_attr: bool,
}

impl CfgFinder {
    fn has_cfg_or_cfg_attr(annotatable: &Annotatable) -> bool {
        let mut finder = CfgFinder { has_cfg_or_cfg_attr: false };
        match annotatable {
            Annotatable::Item(item) => finder.visit_item(&item),
            Annotatable::TraitItem(item) => finder.visit_assoc_item(&item, visit::AssocCtxt::Trait),
            Annotatable::ImplItem(item) => finder.visit_assoc_item(&item, visit::AssocCtxt::Impl),
            Annotatable::ForeignItem(item) => finder.visit_foreign_item(&item),
            Annotatable::Stmt(stmt) => finder.visit_stmt(&stmt),
            Annotatable::Expr(expr) => finder.visit_expr(&expr),
            Annotatable::Arm(arm) => finder.visit_arm(&arm),
            Annotatable::ExprField(field) => finder.visit_expr_field(&field),
            Annotatable::PatField(field) => finder.visit_pat_field(&field),
            Annotatable::GenericParam(param) => finder.visit_generic_param(&param),
            Annotatable::Param(param) => finder.visit_param(&param),
            Annotatable::FieldDef(field) => finder.visit_field_def(&field),
            Annotatable::Variant(variant) => finder.visit_variant(&variant),
        };
        finder.has_cfg_or_cfg_attr
    }
}

impl<'ast> visit::Visitor<'ast> for CfgFinder {
    fn visit_attribute(&mut self, attr: &'ast Attribute) {
        // We want short-circuiting behavior, so don't use the '|=' operator.
        self.has_cfg_or_cfg_attr = self.has_cfg_or_cfg_attr
            || attr
                .ident()
                .map_or(false, |ident| ident.name == sym::cfg || ident.name == sym::cfg_attr);
    }
}

impl CfgEval<'_, '_> {
    fn configure<T: AstLike>(&mut self, node: T) -> Option<T> {
        self.cfg.configure(node)
    }

    pub fn configure_annotatable(&mut self, mut annotatable: Annotatable) -> Annotatable {
        // Tokenizing and re-parsing the `Annotatable` can have a significant
        // performance impact, so try to avoid it if possible
        if !CfgFinder::has_cfg_or_cfg_attr(&annotatable) {
            return annotatable;
        }

        // The majority of parsed attribute targets will never need to have early cfg-expansion
        // run (e.g. they are not part of a `#[derive]` or `#[cfg_eval]` macro inoput).
        // Therefore, we normally do not capture the necessary information about `#[cfg]`
        // and `#[cfg_attr]` attributes during parsing.
        //
        // Therefore, when we actually *do* run early cfg-expansion, we need to tokenize
        // and re-parse the attribute target, this time capturing information about
        // the location of `#[cfg]` and `#[cfg_attr]` in the token stream. The tokenization
        // process is lossless, so this process is invisible to proc-macros.

        // FIXME - get rid of this clone
        let nt = annotatable.clone().into_nonterminal();

        let mut orig_tokens = rustc_parse::nt_to_tokenstream(
            &nt,
            &self.cfg.sess.parse_sess,
            CanSynthesizeMissingTokens::No,
        );

        // 'Flatten' all nonterminals (i.e. `TokenKind::Interpolated`)
        // to `None`-delimited groups containing the corresponding tokens. This
        // is normally delayed until the proc-macro server actually needs to
        // provide a `TokenKind::Interpolated` to a proc-macro. We do this earlier,
        // so that we can handle cases like:
        //
        // ```rust
        // #[cfg_eval] #[cfg] $item
        //```
        //
        // where `$item` is `#[cfg_attr] struct Foo {}`. We want to make
        // sure to evaluate *all* `#[cfg]` and `#[cfg_attr]` attributes - the simplest
        // way to do this is to do a single parse of a stream without any nonterminals.
        let mut flatten = FlattenNonterminals {
            nt_to_tokenstream: rustc_parse::nt_to_tokenstream,
            parse_sess: &self.cfg.sess.parse_sess,
            synthesize_tokens: CanSynthesizeMissingTokens::No,
        };
        orig_tokens = flatten.process_token_stream(orig_tokens);

        // Re-parse the tokens, setting the `capture_cfg` flag to save extra information
        // to the captured `AttrAnnotatedTokenStream` (specifically, we capture
        // `AttrAnnotatedTokenTree::AttributesData` for all occurences of `#[cfg]` and `#[cfg_attr]`)
        let mut parser =
            rustc_parse::stream_to_parser(&self.cfg.sess.parse_sess, orig_tokens, None);
        parser.capture_cfg = true;
        annotatable = match annotatable {
            Annotatable::Item(_) => {
                Annotatable::Item(parser.parse_item(ForceCollect::Yes).unwrap().unwrap())
            }
            Annotatable::TraitItem(_) => Annotatable::TraitItem(
                parser.parse_trait_item(ForceCollect::Yes).unwrap().unwrap().unwrap(),
            ),
            Annotatable::ImplItem(_) => Annotatable::ImplItem(
                parser.parse_impl_item(ForceCollect::Yes).unwrap().unwrap().unwrap(),
            ),
            Annotatable::ForeignItem(_) => Annotatable::ForeignItem(
                parser.parse_foreign_item(ForceCollect::Yes).unwrap().unwrap().unwrap(),
            ),
            Annotatable::Stmt(_) => {
                Annotatable::Stmt(P(parser.parse_stmt(ForceCollect::Yes).unwrap().unwrap()))
            }
            Annotatable::Expr(_) => Annotatable::Expr(parser.parse_expr_force_collect().unwrap()),
            _ => unreachable!(),
        };

        // Now that we have our re-parsed `AttrAnnotatedTokenStream`, recursively configuring
        // our attribute target will correctly the tokens as well.
        flat_map_annotatable(self, annotatable)
    }
}

impl MutVisitor for CfgEval<'_, '_> {
    fn visit_expr(&mut self, expr: &mut P<ast::Expr>) {
        self.cfg.configure_expr(expr);
        mut_visit::noop_visit_expr(expr, self);
    }

    fn filter_map_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
        let mut expr = configure!(self, expr);
        mut_visit::noop_visit_expr(&mut expr, self);
        Some(expr)
    }

    fn flat_map_generic_param(
        &mut self,
        param: ast::GenericParam,
    ) -> SmallVec<[ast::GenericParam; 1]> {
        mut_visit::noop_flat_map_generic_param(configure!(self, param), self)
    }

    fn flat_map_stmt(&mut self, stmt: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> {
        mut_visit::noop_flat_map_stmt(configure!(self, stmt), self)
    }

    fn flat_map_item(&mut self, item: P<ast::Item>) -> SmallVec<[P<ast::Item>; 1]> {
        mut_visit::noop_flat_map_item(configure!(self, item), self)
    }

    fn flat_map_impl_item(&mut self, item: P<ast::AssocItem>) -> SmallVec<[P<ast::AssocItem>; 1]> {
        mut_visit::noop_flat_map_assoc_item(configure!(self, item), self)
    }

    fn flat_map_trait_item(&mut self, item: P<ast::AssocItem>) -> SmallVec<[P<ast::AssocItem>; 1]> {
        mut_visit::noop_flat_map_assoc_item(configure!(self, item), self)
    }

    fn flat_map_foreign_item(
        &mut self,
        foreign_item: P<ast::ForeignItem>,
    ) -> SmallVec<[P<ast::ForeignItem>; 1]> {
        mut_visit::noop_flat_map_foreign_item(configure!(self, foreign_item), self)
    }

    fn flat_map_arm(&mut self, arm: ast::Arm) -> SmallVec<[ast::Arm; 1]> {
        mut_visit::noop_flat_map_arm(configure!(self, arm), self)
    }

    fn flat_map_expr_field(&mut self, field: ast::ExprField) -> SmallVec<[ast::ExprField; 1]> {
        mut_visit::noop_flat_map_expr_field(configure!(self, field), self)
    }

    fn flat_map_pat_field(&mut self, fp: ast::PatField) -> SmallVec<[ast::PatField; 1]> {
        mut_visit::noop_flat_map_pat_field(configure!(self, fp), self)
    }

    fn flat_map_param(&mut self, p: ast::Param) -> SmallVec<[ast::Param; 1]> {
        mut_visit::noop_flat_map_param(configure!(self, p), self)
    }

    fn flat_map_field_def(&mut self, sf: ast::FieldDef) -> SmallVec<[ast::FieldDef; 1]> {
        mut_visit::noop_flat_map_field_def(configure!(self, sf), self)
    }

    fn flat_map_variant(&mut self, variant: ast::Variant) -> SmallVec<[ast::Variant; 1]> {
        mut_visit::noop_flat_map_variant(configure!(self, variant), self)
    }
}
<commit_msg>cfg_eval: Replace multiple `unwrap`s with a single `unwrap`<commit_after>use crate::util::check_builtin_macro_attribute;

use rustc_ast as ast;
use rustc_ast::mut_visit::MutVisitor;
use rustc_ast::tokenstream::CanSynthesizeMissingTokens;
use rustc_ast::visit::Visitor;
use rustc_ast::{mut_visit, visit};
use rustc_ast::{AstLike, Attribute};
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_expand::config::StripUnconfigured;
use rustc_expand::configure;
use rustc_parse::parser::ForceCollect;
use rustc_session::utils::FlattenNonterminals;

use rustc_ast::ptr::P;
use rustc_span::symbol::sym;
use rustc_span::Span;
use smallvec::SmallVec;

crate fn expand(
    ecx: &mut ExtCtxt<'_>,
    _span: Span,
    meta_item: &ast::MetaItem,
    annotatable: Annotatable,
) -> Vec<Annotatable> {
    check_builtin_macro_attribute(ecx, meta_item, sym::cfg_eval);
    vec![cfg_eval(ecx, annotatable)]
}

crate fn cfg_eval(ecx: &ExtCtxt<'_>, annotatable: Annotatable) -> Annotatable {
    CfgEval {
        cfg: &mut StripUnconfigured {
            sess: ecx.sess,
            features: ecx.ecfg.features,
            config_tokens: true,
        },
    }
    .configure_annotatable(annotatable)
    // Since the item itself has already been configured by the `InvocationCollector`,
    // we know that fold result vector will contain exactly one element.
    .unwrap()
}

struct CfgEval<'a, 'b> {
    cfg: &'a mut StripUnconfigured<'b>,
}

fn flat_map_annotatable(
    vis: &mut impl MutVisitor,
    annotatable: Annotatable,
) -> Option<Annotatable> {
    match annotatable {
        Annotatable::Item(item) => vis.flat_map_item(item).pop().map(Annotatable::Item),
        Annotatable::TraitItem(item) => {
            vis.flat_map_trait_item(item).pop().map(Annotatable::TraitItem)
        }
        Annotatable::ImplItem(item) => {
            vis.flat_map_impl_item(item).pop().map(Annotatable::ImplItem)
        }
        Annotatable::ForeignItem(item) => {
            vis.flat_map_foreign_item(item).pop().map(Annotatable::ForeignItem)
        }
        Annotatable::Stmt(stmt) => {
            vis.flat_map_stmt(stmt.into_inner()).pop().map(P).map(Annotatable::Stmt)
        }
        Annotatable::Expr(mut expr) => {
            vis.visit_expr(&mut expr);
            Some(Annotatable::Expr(expr))
        }
        Annotatable::Arm(arm) => vis.flat_map_arm(arm).pop().map(Annotatable::Arm),
        Annotatable::ExprField(field) => {
            vis.flat_map_expr_field(field).pop().map(Annotatable::ExprField)
        }
        Annotatable::PatField(fp) => vis.flat_map_pat_field(fp).pop().map(Annotatable::PatField),
        Annotatable::GenericParam(param) => {
            vis.flat_map_generic_param(param).pop().map(Annotatable::GenericParam)
        }
        Annotatable::Param(param) => vis.flat_map_param(param).pop().map(Annotatable::Param),
        Annotatable::FieldDef(sf) => vis.flat_map_field_def(sf).pop().map(Annotatable::FieldDef),
        Annotatable::Variant(v) => vis.flat_map_variant(v).pop().map(Annotatable::Variant),
    }
}

struct CfgFinder {
    has_cfg_or_cfg_attr: bool,
}

impl CfgFinder {
    fn has_cfg_or_cfg_attr(annotatable: &Annotatable) -> bool {
        let mut finder = CfgFinder { has_cfg_or_cfg_attr: false };
        match annotatable {
            Annotatable::Item(item) => finder.visit_item(&item),
            Annotatable::TraitItem(item) => finder.visit_assoc_item(&item, visit::AssocCtxt::Trait),
            Annotatable::ImplItem(item) => finder.visit_assoc_item(&item, visit::AssocCtxt::Impl),
            Annotatable::ForeignItem(item) => finder.visit_foreign_item(&item),
            Annotatable::Stmt(stmt) => finder.visit_stmt(&stmt),
            Annotatable::Expr(expr) => finder.visit_expr(&expr),
            Annotatable::Arm(arm) => finder.visit_arm(&arm),
            Annotatable::ExprField(field) => finder.visit_expr_field(&field),
            Annotatable::PatField(field) => finder.visit_pat_field(&field),
            Annotatable::GenericParam(param) => finder.visit_generic_param(&param),
            Annotatable::Param(param) => finder.visit_param(&param),
            Annotatable::FieldDef(field) => finder.visit_field_def(&field),
            Annotatable::Variant(variant) => finder.visit_variant(&variant),
        };
        finder.has_cfg_or_cfg_attr
    }
}

impl<'ast> visit::Visitor<'ast> for CfgFinder {
    fn visit_attribute(&mut self, attr: &'ast Attribute) {
        // We want short-circuiting behavior, so don't use the '|=' operator.
        self.has_cfg_or_cfg_attr = self.has_cfg_or_cfg_attr
            || attr
                .ident()
                .map_or(false, |ident| ident.name == sym::cfg || ident.name == sym::cfg_attr);
    }
}

impl CfgEval<'_, '_> {
    fn configure<T: AstLike>(&mut self, node: T) -> Option<T> {
        self.cfg.configure(node)
    }

    fn configure_annotatable(&mut self, mut annotatable: Annotatable) -> Option<Annotatable> {
        // Tokenizing and re-parsing the `Annotatable` can have a significant
        // performance impact, so try to avoid it if possible
        if !CfgFinder::has_cfg_or_cfg_attr(&annotatable) {
            return Some(annotatable);
        }

        // The majority of parsed attribute targets will never need to have early cfg-expansion
        // run (e.g. they are not part of a `#[derive]` or `#[cfg_eval]` macro inoput).
        // Therefore, we normally do not capture the necessary information about `#[cfg]`
        // and `#[cfg_attr]` attributes during parsing.
        //
        // Therefore, when we actually *do* run early cfg-expansion, we need to tokenize
        // and re-parse the attribute target, this time capturing information about
        // the location of `#[cfg]` and `#[cfg_attr]` in the token stream. The tokenization
        // process is lossless, so this process is invisible to proc-macros.

        // FIXME - get rid of this clone
        let nt = annotatable.clone().into_nonterminal();

        let mut orig_tokens = rustc_parse::nt_to_tokenstream(
            &nt,
            &self.cfg.sess.parse_sess,
            CanSynthesizeMissingTokens::No,
        );

        // 'Flatten' all nonterminals (i.e. `TokenKind::Interpolated`)
        // to `None`-delimited groups containing the corresponding tokens. This
        // is normally delayed until the proc-macro server actually needs to
        // provide a `TokenKind::Interpolated` to a proc-macro. We do this earlier,
        // so that we can handle cases like:
        //
        // ```rust
        // #[cfg_eval] #[cfg] $item
        //```
        //
        // where `$item` is `#[cfg_attr] struct Foo {}`. We want to make
        // sure to evaluate *all* `#[cfg]` and `#[cfg_attr]` attributes - the simplest
        // way to do this is to do a single parse of a stream without any nonterminals.
        let mut flatten = FlattenNonterminals {
            nt_to_tokenstream: rustc_parse::nt_to_tokenstream,
            parse_sess: &self.cfg.sess.parse_sess,
            synthesize_tokens: CanSynthesizeMissingTokens::No,
        };
        orig_tokens = flatten.process_token_stream(orig_tokens);

        // Re-parse the tokens, setting the `capture_cfg` flag to save extra information
        // to the captured `AttrAnnotatedTokenStream` (specifically, we capture
        // `AttrAnnotatedTokenTree::AttributesData` for all occurences of `#[cfg]` and `#[cfg_attr]`)
        let mut parser =
            rustc_parse::stream_to_parser(&self.cfg.sess.parse_sess, orig_tokens, None);
        parser.capture_cfg = true;
        annotatable = match annotatable {
            Annotatable::Item(_) => {
                Annotatable::Item(parser.parse_item(ForceCollect::Yes).unwrap().unwrap())
            }
            Annotatable::TraitItem(_) => Annotatable::TraitItem(
                parser.parse_trait_item(ForceCollect::Yes).unwrap().unwrap().unwrap(),
            ),
            Annotatable::ImplItem(_) => Annotatable::ImplItem(
                parser.parse_impl_item(ForceCollect::Yes).unwrap().unwrap().unwrap(),
            ),
            Annotatable::ForeignItem(_) => Annotatable::ForeignItem(
                parser.parse_foreign_item(ForceCollect::Yes).unwrap().unwrap().unwrap(),
            ),
            Annotatable::Stmt(_) => {
                Annotatable::Stmt(P(parser.parse_stmt(ForceCollect::Yes).unwrap().unwrap()))
            }
            Annotatable::Expr(_) => Annotatable::Expr(parser.parse_expr_force_collect().unwrap()),
            _ => unreachable!(),
        };

        // Now that we have our re-parsed `AttrAnnotatedTokenStream`, recursively configuring
        // our attribute target will correctly the tokens as well.
        flat_map_annotatable(self, annotatable)
    }
}

impl MutVisitor for CfgEval<'_, '_> {
    fn visit_expr(&mut self, expr: &mut P<ast::Expr>) {
        self.cfg.configure_expr(expr);
        mut_visit::noop_visit_expr(expr, self);
    }

    fn filter_map_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
        let mut expr = configure!(self, expr);
        mut_visit::noop_visit_expr(&mut expr, self);
        Some(expr)
    }

    fn flat_map_generic_param(
        &mut self,
        param: ast::GenericParam,
    ) -> SmallVec<[ast::GenericParam; 1]> {
        mut_visit::noop_flat_map_generic_param(configure!(self, param), self)
    }

    fn flat_map_stmt(&mut self, stmt: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> {
        mut_visit::noop_flat_map_stmt(configure!(self, stmt), self)
    }

    fn flat_map_item(&mut self, item: P<ast::Item>) -> SmallVec<[P<ast::Item>; 1]> {
        mut_visit::noop_flat_map_item(configure!(self, item), self)
    }

    fn flat_map_impl_item(&mut self, item: P<ast::AssocItem>) -> SmallVec<[P<ast::AssocItem>; 1]> {
        mut_visit::noop_flat_map_assoc_item(configure!(self, item), self)
    }

    fn flat_map_trait_item(&mut self, item: P<ast::AssocItem>) -> SmallVec<[P<ast::AssocItem>; 1]> {
        mut_visit::noop_flat_map_assoc_item(configure!(self, item), self)
    }

    fn flat_map_foreign_item(
        &mut self,
        foreign_item: P<ast::ForeignItem>,
    ) -> SmallVec<[P<ast::ForeignItem>; 1]> {
        mut_visit::noop_flat_map_foreign_item(configure!(self, foreign_item), self)
    }

    fn flat_map_arm(&mut self, arm: ast::Arm) -> SmallVec<[ast::Arm; 1]> {
        mut_visit::noop_flat_map_arm(configure!(self, arm), self)
    }

    fn flat_map_expr_field(&mut self, field: ast::ExprField) -> SmallVec<[ast::ExprField; 1]> {
        mut_visit::noop_flat_map_expr_field(configure!(self, field), self)
    }

    fn flat_map_pat_field(&mut self, fp: ast::PatField) -> SmallVec<[ast::PatField; 1]> {
        mut_visit::noop_flat_map_pat_field(configure!(self, fp), self)
    }

    fn flat_map_param(&mut self, p: ast::Param) -> SmallVec<[ast::Param; 1]> {
        mut_visit::noop_flat_map_param(configure!(self, p), self)
    }

    fn flat_map_field_def(&mut self, sf: ast::FieldDef) -> SmallVec<[ast::FieldDef; 1]> {
        mut_visit::noop_flat_map_field_def(configure!(self, sf), self)
    }

    fn flat_map_variant(&mut self, variant: ast::Variant) -> SmallVec<[ast::Variant; 1]> {
        mut_visit::noop_flat_map_variant(configure!(self, variant), self)
    }
}
<|endoftext|>


<commit_before>// Copyright 2013-2015, The Rust-GNOME Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

//! Date and Time Functions — calendrical calculations and miscellaneous time stuff

use libc::{c_int, c_uint, c_ulong};
use ffi;
use std;
use super::translate::ToGlibPtr;

/// Simply a replacement for time_t. It has been deprecated since it is not equivalent to time_t on 64-bit platforms with a 64-bit time_t.
/// Unrelated to GTimer.
///
/// Note that GTime is defined to always be a 32-bit integer, unlike time_t which may be 64-bit on some systems. Therefore, GTime will
/// overflow in the year 2038, and you cannot use the address of a GTime variable as argument to the UNIX time() function.
pub type Time = i32;

/// Integer representing a year; G_DATE_BAD_YEAR is the invalid value. The year must be 1 or higher; negative (BC) years are not allowed.
/// The year is represented with four digits.
pub type Year = u16;
/// Integer representing a day of the month; between 1 and 31. G_DATE_BAD_DAY represents an invalid day of the month.
pub type Day = u8;

/// Enumeration representing a month; values are G_DATE_JANUARY, G_DATE_FEBRUARY, etc. G_DATE_BAD_MONTH is the invalid value.
#[derive(Clone, Copy)]
#[repr(C)]
pub enum Month {
    /// invalid value
    BadMonth,
    /// january
    January,
    /// february
    February,
    /// march
    March,
    /// april
    April,
    /// may
    May,
    /// june
    June,
    /// july
    July,
    /// august
    August,
    /// september
    September,
    /// october
    October,
    /// november
    November,
    /// december
    December
}

/// Enumeration representing a day of the week; G_DATE_MONDAY, G_DATE_TUESDAY, etc. G_DATE_BAD_WEEKDAY is an invalid weekday.
#[derive(Clone, Copy)]
#[repr(C)]
pub enum Weekday {
    /// invalid value
    BadWeekday,
    /// monday
    Monday,
    /// tuesday
    Tuesday,
    /// wednesday
    Wednesday,
    /// thrusday
    Thursday,
    /// friday
    Friday,
    /// saturday
    Saturday,
    /// sunday
    Sunday
}

pub struct Date {
    pointer: *mut ffi::GDate
}

impl Date {
    pub fn new() -> Option<Date> {
        let tmp = unsafe { ffi::g_date_new() };

        if tmp.is_null() {
            Some(Date {
                pointer: tmp
            })
        } else {
            None
        }
    }

    pub fn new_dmy(day: Day, month: Month, year: Year) -> Option<Date> {
        let tmp = unsafe { ffi::g_date_new_dmy(day as c_int, month as c_int, year) };

        if tmp.is_null() {
            Some(Date {
                pointer: tmp
            })
        } else {
            None
        }
    }

    pub fn new_julian(julian_day: u32) -> Option<Date> {
        let tmp = unsafe { ffi::g_date_new_julian(julian_day) };

        if tmp.is_null() {
            Some(Date {
                pointer: tmp
            })
        } else {
            None
        }
    }

    pub fn clear(&mut self) {
        unsafe { ffi::g_date_clear(self.pointer, 1) }
    }

    pub fn set_day(&mut self, day: Day) {
        unsafe { ffi::g_date_set_day(self.pointer, day as c_int) }
    }

    pub fn set_month(&mut self, month: Month) {
        unsafe { ffi::g_date_set_month(self.pointer, month as c_int) }
    }

    pub fn set_year(&mut self, year: Year) {
        unsafe { ffi::g_date_set_year(self.pointer, year) }
    }

    pub fn set_dmy(&mut self, day: Day, month: Month, year: Year) {
        unsafe { ffi::g_date_set_dmy(self.pointer, day as c_int, month as c_int, year) }
    }

    pub fn set_julian(&mut self, julian: u32) {
        unsafe { ffi::g_date_set_julian(self.pointer, julian) }
    }

    pub fn set_time_t(&mut self, timet: i64) {
        unsafe { ffi::g_date_set_time_t(self.pointer, timet) }
    }

    pub fn set_time_val(&mut self, timeval: &mut TimeVal) {
        unsafe { ffi::g_date_set_time_val(self.pointer, std::mem::transmute(timeval)) }
    }

    pub fn set_parse(&mut self, str_: &str) {
        unsafe { ffi::g_date_set_parse(self.pointer, str_.to_glib_none().0) }
    }

    pub fn add_days(&mut self, days: usize) {
        unsafe { ffi::g_date_add_days(self.pointer, days as c_uint) }
    }

    pub fn subtract_days(&mut self, days: usize) {
        unsafe { ffi::g_date_subtract_days(self.pointer, days as c_uint) }
    }

    pub fn add_months(&mut self, months: usize) {
        unsafe { ffi::g_date_add_months(self.pointer, months as c_uint) }
    }

    pub fn subtract_months(&mut self, months: usize) {
        unsafe { ffi::g_date_subtract_months(self.pointer, months as c_uint) }
    }

    pub fn add_years(&mut self, years: usize) {
        unsafe { ffi::g_date_add_years(self.pointer, years as c_uint) }
    }

    pub fn subtract_years(&mut self, years: usize) {
        unsafe { ffi::g_date_subtract_years(self.pointer, years as c_uint) }
    }

    pub fn days_between(&self, other: &Date) -> isize {
        unsafe { ffi::g_date_days_between(self.pointer, other.pointer) as isize }
    }

    pub fn compare(&self, other: &Date) -> isize {
        unsafe { ffi::g_date_compare(self.pointer, other.pointer) as isize   }
    }

    pub fn clamp(&mut self, min_date: &Date, max_date: &Date) {
        unsafe { ffi::g_date_clamp(self.pointer, min_date.pointer, max_date.pointer) }
    }

    pub fn order(&mut self, other: &mut Date) {
        unsafe { ffi::g_date_order(self.pointer, other.pointer) }
    }

    pub fn get_day(&self) -> Day {
        unsafe { ffi::g_date_get_day(self.pointer) }
    }

    pub fn get_month(&self) -> Month {
        unsafe { std::mem::transmute(ffi::g_date_get_month(self.pointer)) }
    }

    pub fn get_year(&self) -> Year {
        unsafe { ffi::g_date_get_year(self.pointer) }
    }

    pub fn get_julian(&self) -> u32 {
        unsafe { ffi::g_date_get_julian(self.pointer) }
    }

    pub fn get_weekday(&self) -> Weekday {
        unsafe { std::mem::transmute(ffi::g_date_get_weekday(self.pointer)) }
    }

    pub fn get_day_of_year(&self) -> u32 {
        unsafe { ffi::g_date_get_day_of_year(self.pointer) }
    }

    pub fn is_first_of_month(&self) -> bool {
        unsafe { super::to_bool(ffi::g_date_is_first_of_month(self.pointer)) }
    }

    pub fn is_last_of_month(&self) -> bool {
        unsafe { super::to_bool(ffi::g_date_is_last_of_month(self.pointer)) }
    }

    pub fn get_monday_week_of_year(&self) -> u32 {
        unsafe { ffi::g_date_get_monday_week_of_year(self.pointer) }
    }

    pub fn get_sunday_week_of_year(&self) -> u32 {
        unsafe { ffi::g_date_get_sunday_week_of_year(self.pointer) }
    }

    pub fn get_iso8601_week_of_year(&self) -> u32 {
        unsafe { ffi::g_date_get_iso8601_week_of_year(self.pointer) }
    }

    /*pub fn strftime(&self, s: &mut String, format: &str) -> u32 {
        unsafe { ffi::g_date_strftime(self.pointer) }
    }*/

    pub fn is_valid(&self) -> bool {
        unsafe { super::to_bool(ffi::g_date_valid(self.pointer)) }
    }
}

impl Drop for Date {
    fn drop(&mut self) {
        if !self.pointer.is_null() {
            unsafe { ffi::g_date_free(self.pointer); }
            self.pointer = std::ptr::null_mut();
        }
    }
}

/// Represents a precise time, with seconds and microseconds. Similar to the struct timeval returned by the gettimeofday() UNIX
/// system call.
///
/// GLib is attempting to unify around the use of 64bit integers to represent microsecond-precision time. As such, this type
/// will be removed from a future version of GLib.
#[repr(C)]
pub struct TimeVal {
    /// seconds
    pub tv_sec: i32,
    /// microseconds
    pub tv_usec: i32
}

impl TimeVal {
    pub fn add(&mut self, microseconds: u64) {
        unsafe { ffi::g_time_val_add(std::mem::transmute(self), microseconds as c_ulong) }
    }

    pub fn from_iso8601(&mut self, iso_date: &str) {
        unsafe { ffi::g_time_val_from_iso8601(iso_date.to_glib_none().0, std::mem::transmute(self)) }
    }

    pub fn to_iso8601(&mut self) -> Option<String> {
        unsafe { ::translate::from_glib_none(ffi::g_time_val_to_iso8601(std::mem::transmute(self))) }
    }
}

pub fn get_current_time() -> TimeVal {
    let mut t = TimeVal {
        tv_sec: 0,
        tv_usec: 0
    };

    unsafe { ffi::g_get_current_time(std::mem::transmute(&mut t)) };
    t
}

pub fn usleep(microseconds: u64) {
    unsafe { ffi::g_usleep(microseconds as c_ulong) }
}

pub fn get_monotonic_time() -> i64 {
    unsafe { ffi::g_get_monotonic_time() }
}

pub fn get_real_time() -> i64 {
    unsafe { ffi::g_get_real_time() }
}

pub fn get_days_in_month(month: Month, year: Year) -> u8 {
    unsafe { ffi::g_date_get_days_in_month(month as c_int, year) }
}

pub fn is_leap_year(year: Year) -> bool {
    unsafe { super::to_bool(ffi::g_date_is_leap_year(year)) }
}

pub fn get_monday_weeks_in_year(year: Year) -> u8 {
    unsafe { ffi::g_date_get_monday_weeks_in_year(year) }
}

pub fn get_sunday_weeks_in_year(year: Year) -> u8 {
    unsafe { ffi::g_date_get_sunday_weeks_in_year(year) }
}

pub fn is_valid_day(day: Day) -> bool {
    unsafe { super::to_bool(ffi::g_date_valid_day(day as c_int)) }
}

pub fn is_valid_month(month: Month) -> bool {
    unsafe { super::to_bool(ffi::g_date_valid_month(month as c_int)) }
}

pub fn is_valid_year(year: Year) -> bool {
    unsafe { super::to_bool(ffi::g_date_valid_year(year)) }
}

pub fn is_valid_dmy(day: Day, month: Month, year: Year) -> bool {
    unsafe { super::to_bool(ffi::g_date_valid_dmy(day as c_int, month as c_int, year)) }
}

pub fn is_valid_julian(julian: u32) -> bool {
    unsafe { super::to_bool(ffi::g_date_valid_julian(julian)) }
}

pub fn is_valid_weekday(day: Weekday) -> bool {
    unsafe { super::to_bool(ffi::g_date_valid_weekday(day as c_int)) }
}
<commit_msg>TimeVal fields are glong not gint<commit_after>// Copyright 2013-2015, The Rust-GNOME Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

//! Date and Time Functions — calendrical calculations and miscellaneous time stuff

use libc::{c_int, c_uint, c_long, c_ulong};
use ffi;
use std;
use super::translate::ToGlibPtr;

/// Simply a replacement for time_t. It has been deprecated since it is not equivalent to time_t on 64-bit platforms with a 64-bit time_t.
/// Unrelated to GTimer.
///
/// Note that GTime is defined to always be a 32-bit integer, unlike time_t which may be 64-bit on some systems. Therefore, GTime will
/// overflow in the year 2038, and you cannot use the address of a GTime variable as argument to the UNIX time() function.
pub type Time = i32;

/// Integer representing a year; G_DATE_BAD_YEAR is the invalid value. The year must be 1 or higher; negative (BC) years are not allowed.
/// The year is represented with four digits.
pub type Year = u16;
/// Integer representing a day of the month; between 1 and 31. G_DATE_BAD_DAY represents an invalid day of the month.
pub type Day = u8;

/// Enumeration representing a month; values are G_DATE_JANUARY, G_DATE_FEBRUARY, etc. G_DATE_BAD_MONTH is the invalid value.
#[derive(Clone, Copy)]
#[repr(C)]
pub enum Month {
    /// invalid value
    BadMonth,
    /// january
    January,
    /// february
    February,
    /// march
    March,
    /// april
    April,
    /// may
    May,
    /// june
    June,
    /// july
    July,
    /// august
    August,
    /// september
    September,
    /// october
    October,
    /// november
    November,
    /// december
    December
}

/// Enumeration representing a day of the week; G_DATE_MONDAY, G_DATE_TUESDAY, etc. G_DATE_BAD_WEEKDAY is an invalid weekday.
#[derive(Clone, Copy)]
#[repr(C)]
pub enum Weekday {
    /// invalid value
    BadWeekday,
    /// monday
    Monday,
    /// tuesday
    Tuesday,
    /// wednesday
    Wednesday,
    /// thrusday
    Thursday,
    /// friday
    Friday,
    /// saturday
    Saturday,
    /// sunday
    Sunday
}

pub struct Date {
    pointer: *mut ffi::GDate
}

impl Date {
    pub fn new() -> Option<Date> {
        let tmp = unsafe { ffi::g_date_new() };

        if tmp.is_null() {
            Some(Date {
                pointer: tmp
            })
        } else {
            None
        }
    }

    pub fn new_dmy(day: Day, month: Month, year: Year) -> Option<Date> {
        let tmp = unsafe { ffi::g_date_new_dmy(day as c_int, month as c_int, year) };

        if tmp.is_null() {
            Some(Date {
                pointer: tmp
            })
        } else {
            None
        }
    }

    pub fn new_julian(julian_day: u32) -> Option<Date> {
        let tmp = unsafe { ffi::g_date_new_julian(julian_day) };

        if tmp.is_null() {
            Some(Date {
                pointer: tmp
            })
        } else {
            None
        }
    }

    pub fn clear(&mut self) {
        unsafe { ffi::g_date_clear(self.pointer, 1) }
    }

    pub fn set_day(&mut self, day: Day) {
        unsafe { ffi::g_date_set_day(self.pointer, day as c_int) }
    }

    pub fn set_month(&mut self, month: Month) {
        unsafe { ffi::g_date_set_month(self.pointer, month as c_int) }
    }

    pub fn set_year(&mut self, year: Year) {
        unsafe { ffi::g_date_set_year(self.pointer, year) }
    }

    pub fn set_dmy(&mut self, day: Day, month: Month, year: Year) {
        unsafe { ffi::g_date_set_dmy(self.pointer, day as c_int, month as c_int, year) }
    }

    pub fn set_julian(&mut self, julian: u32) {
        unsafe { ffi::g_date_set_julian(self.pointer, julian) }
    }

    pub fn set_time_t(&mut self, timet: i64) {
        unsafe { ffi::g_date_set_time_t(self.pointer, timet) }
    }

    pub fn set_time_val(&mut self, timeval: &mut TimeVal) {
        unsafe { ffi::g_date_set_time_val(self.pointer, std::mem::transmute(timeval)) }
    }

    pub fn set_parse(&mut self, str_: &str) {
        unsafe { ffi::g_date_set_parse(self.pointer, str_.to_glib_none().0) }
    }

    pub fn add_days(&mut self, days: usize) {
        unsafe { ffi::g_date_add_days(self.pointer, days as c_uint) }
    }

    pub fn subtract_days(&mut self, days: usize) {
        unsafe { ffi::g_date_subtract_days(self.pointer, days as c_uint) }
    }

    pub fn add_months(&mut self, months: usize) {
        unsafe { ffi::g_date_add_months(self.pointer, months as c_uint) }
    }

    pub fn subtract_months(&mut self, months: usize) {
        unsafe { ffi::g_date_subtract_months(self.pointer, months as c_uint) }
    }

    pub fn add_years(&mut self, years: usize) {
        unsafe { ffi::g_date_add_years(self.pointer, years as c_uint) }
    }

    pub fn subtract_years(&mut self, years: usize) {
        unsafe { ffi::g_date_subtract_years(self.pointer, years as c_uint) }
    }

    pub fn days_between(&self, other: &Date) -> isize {
        unsafe { ffi::g_date_days_between(self.pointer, other.pointer) as isize }
    }

    pub fn compare(&self, other: &Date) -> isize {
        unsafe { ffi::g_date_compare(self.pointer, other.pointer) as isize   }
    }

    pub fn clamp(&mut self, min_date: &Date, max_date: &Date) {
        unsafe { ffi::g_date_clamp(self.pointer, min_date.pointer, max_date.pointer) }
    }

    pub fn order(&mut self, other: &mut Date) {
        unsafe { ffi::g_date_order(self.pointer, other.pointer) }
    }

    pub fn get_day(&self) -> Day {
        unsafe { ffi::g_date_get_day(self.pointer) }
    }

    pub fn get_month(&self) -> Month {
        unsafe { std::mem::transmute(ffi::g_date_get_month(self.pointer)) }
    }

    pub fn get_year(&self) -> Year {
        unsafe { ffi::g_date_get_year(self.pointer) }
    }

    pub fn get_julian(&self) -> u32 {
        unsafe { ffi::g_date_get_julian(self.pointer) }
    }

    pub fn get_weekday(&self) -> Weekday {
        unsafe { std::mem::transmute(ffi::g_date_get_weekday(self.pointer)) }
    }

    pub fn get_day_of_year(&self) -> u32 {
        unsafe { ffi::g_date_get_day_of_year(self.pointer) }
    }

    pub fn is_first_of_month(&self) -> bool {
        unsafe { super::to_bool(ffi::g_date_is_first_of_month(self.pointer)) }
    }

    pub fn is_last_of_month(&self) -> bool {
        unsafe { super::to_bool(ffi::g_date_is_last_of_month(self.pointer)) }
    }

    pub fn get_monday_week_of_year(&self) -> u32 {
        unsafe { ffi::g_date_get_monday_week_of_year(self.pointer) }
    }

    pub fn get_sunday_week_of_year(&self) -> u32 {
        unsafe { ffi::g_date_get_sunday_week_of_year(self.pointer) }
    }

    pub fn get_iso8601_week_of_year(&self) -> u32 {
        unsafe { ffi::g_date_get_iso8601_week_of_year(self.pointer) }
    }

    /*pub fn strftime(&self, s: &mut String, format: &str) -> u32 {
        unsafe { ffi::g_date_strftime(self.pointer) }
    }*/

    pub fn is_valid(&self) -> bool {
        unsafe { super::to_bool(ffi::g_date_valid(self.pointer)) }
    }
}

impl Drop for Date {
    fn drop(&mut self) {
        if !self.pointer.is_null() {
            unsafe { ffi::g_date_free(self.pointer); }
            self.pointer = std::ptr::null_mut();
        }
    }
}

/// Represents a precise time, with seconds and microseconds. Similar to the struct timeval returned by the gettimeofday() UNIX
/// system call.
///
/// GLib is attempting to unify around the use of 64bit integers to represent microsecond-precision time. As such, this type
/// will be removed from a future version of GLib.
#[repr(C)]
pub struct TimeVal {
    /// seconds
    pub tv_sec: c_long,
    /// microseconds
    pub tv_usec: c_long,
}

impl TimeVal {
    pub fn add(&mut self, microseconds: u64) {
        unsafe { ffi::g_time_val_add(std::mem::transmute(self), microseconds as c_ulong) }
    }

    pub fn from_iso8601(&mut self, iso_date: &str) {
        unsafe { ffi::g_time_val_from_iso8601(iso_date.to_glib_none().0, std::mem::transmute(self)) }
    }

    pub fn to_iso8601(&mut self) -> Option<String> {
        unsafe { ::translate::from_glib_none(ffi::g_time_val_to_iso8601(std::mem::transmute(self))) }
    }
}

pub fn get_current_time() -> TimeVal {
    let mut t = TimeVal {
        tv_sec: 0,
        tv_usec: 0
    };

    unsafe { ffi::g_get_current_time(std::mem::transmute(&mut t)) };
    t
}

pub fn usleep(microseconds: u64) {
    unsafe { ffi::g_usleep(microseconds as c_ulong) }
}

pub fn get_monotonic_time() -> i64 {
    unsafe { ffi::g_get_monotonic_time() }
}

pub fn get_real_time() -> i64 {
    unsafe { ffi::g_get_real_time() }
}

pub fn get_days_in_month(month: Month, year: Year) -> u8 {
    unsafe { ffi::g_date_get_days_in_month(month as c_int, year) }
}

pub fn is_leap_year(year: Year) -> bool {
    unsafe { super::to_bool(ffi::g_date_is_leap_year(year)) }
}

pub fn get_monday_weeks_in_year(year: Year) -> u8 {
    unsafe { ffi::g_date_get_monday_weeks_in_year(year) }
}

pub fn get_sunday_weeks_in_year(year: Year) -> u8 {
    unsafe { ffi::g_date_get_sunday_weeks_in_year(year) }
}

pub fn is_valid_day(day: Day) -> bool {
    unsafe { super::to_bool(ffi::g_date_valid_day(day as c_int)) }
}

pub fn is_valid_month(month: Month) -> bool {
    unsafe { super::to_bool(ffi::g_date_valid_month(month as c_int)) }
}

pub fn is_valid_year(year: Year) -> bool {
    unsafe { super::to_bool(ffi::g_date_valid_year(year)) }
}

pub fn is_valid_dmy(day: Day, month: Month, year: Year) -> bool {
    unsafe { super::to_bool(ffi::g_date_valid_dmy(day as c_int, month as c_int, year)) }
}

pub fn is_valid_julian(julian: u32) -> bool {
    unsafe { super::to_bool(ffi::g_date_valid_julian(julian)) }
}

pub fn is_valid_weekday(day: Weekday) -> bool {
    unsafe { super::to_bool(ffi::g_date_valid_weekday(day as c_int)) }
}
<|endoftext|>


<commit_before>use crate::{api_client::{self,
                         Client},
            common::ui::{Status,
                         UIWriter,
                         UI},
            error::{Error,
                    Result},
            PRODUCT,
            VERSION};
use reqwest::StatusCode;

pub fn start(ui: &mut UI, bldr_url: &str, token: &str, origin: &str, account: &str) -> Result<()> {
    let api_client = Client::new(bldr_url, PRODUCT, VERSION, None).map_err(Error::APIClient)?;

    ui.status(Status::Transferring,
              format!("ownership of origin {} to {}.", origin, account))?;

    match api_client.transfer_origin_ownership(origin, token, account) {
        Ok(_) => {
            ui.status(Status::Transferred, "ownership successfully!".to_string())
              .or(Ok(()))
        }
        Err(api_client::Error::APIError(StatusCode::FORBIDDEN, msg)) => {
            ui.fatal("Failed to transfer origin ownership!")?;
            // TODO: remove the "the operation has already been completed" phrase
            // when this issue is closed: https://github.com/habitat-sh/builder/issues/1273
            ui.fatal("Either you are not the current owner, the operation requested has already \
                      been completed, or the new owner is not yet a member.")?;
            Err(Error::APIClient(api_client::Error::APIError(StatusCode::FORBIDDEN, msg)))
        }
        Err(api_client::Error::APIError(StatusCode::UNAUTHORIZED, msg)) => {
            ui.fatal("Failed to transfer origin ownership!")?;
            Err(Error::APIClient(api_client::Error::APIError(StatusCode::UNAUTHORIZED, msg)))
        }
        Err(api_client::Error::APIError(StatusCode::UNPROCESSABLE_ENTITY, msg)) => {
            ui.fatal("Failed to transfer origin ownership!")?;
            ui.fatal("This situation could arise if, for example, you attempted to transfer \
                      ownership to yourself.")?;
            Err(Error::APIClient(api_client::Error::APIError(StatusCode::UNPROCESSABLE_ENTITY,
                                                             msg)))
        }
        Err(api_client::Error::APIError(StatusCode::NOT_FOUND, msg)) => {
            ui.fatal("Failed to transfer origin ownership!")?;
            ui.fatal("The origin or the new origin owner account (or both) does not exist.")?;
            Err(Error::APIClient(api_client::Error::APIError(StatusCode::NOT_FOUND, msg)))
        }
        Err(e) => {
            ui.fatal(format!("Failed to transfer origin {} ownership to {}, {:?}",
                             origin, account, e))?;
            Err(Error::from(e))
        }
    }
}
<commit_msg>rephrasing error message for http 403<commit_after>use crate::{api_client::{self,
                         Client},
            common::ui::{Status,
                         UIWriter,
                         UI},
            error::{Error,
                    Result},
            PRODUCT,
            VERSION};
use reqwest::StatusCode;

pub fn start(ui: &mut UI, bldr_url: &str, token: &str, origin: &str, account: &str) -> Result<()> {
    let api_client = Client::new(bldr_url, PRODUCT, VERSION, None).map_err(Error::APIClient)?;

    ui.status(Status::Transferring,
              format!("ownership of origin {} to {}.", origin, account))?;

    match api_client.transfer_origin_ownership(origin, token, account) {
        Ok(_) => {
            ui.status(Status::Transferred, "ownership successfully!".to_string())
              .or(Ok(()))
        }
        Err(api_client::Error::APIError(StatusCode::FORBIDDEN, msg)) => {
            ui.fatal("Failed to transfer origin ownership!")?;
            ui.fatal("Either you are not the current owner or the new owner is not yet a member.")?;
            Err(Error::APIClient(api_client::Error::APIError(StatusCode::FORBIDDEN, msg)))
        }
        Err(api_client::Error::APIError(StatusCode::UNAUTHORIZED, msg)) => {
            ui.fatal("Failed to transfer origin ownership!")?;
            Err(Error::APIClient(api_client::Error::APIError(StatusCode::UNAUTHORIZED, msg)))
        }
        Err(api_client::Error::APIError(StatusCode::UNPROCESSABLE_ENTITY, msg)) => {
            ui.fatal("Failed to transfer origin ownership!")?;
            ui.fatal("This situation could arise if, for example, you attempted to transfer \
                      ownership to yourself.")?;
            Err(Error::APIClient(api_client::Error::APIError(StatusCode::UNPROCESSABLE_ENTITY,
                                                             msg)))
        }
        Err(api_client::Error::APIError(StatusCode::NOT_FOUND, msg)) => {
            ui.fatal("Failed to transfer origin ownership!")?;
            ui.fatal("The origin or the new origin owner account (or both) does not exist.")?;
            Err(Error::APIClient(api_client::Error::APIError(StatusCode::NOT_FOUND, msg)))
        }
        Err(e) => {
            ui.fatal(format!("Failed to transfer origin {} ownership to {}, {:?}",
                             origin, account, e))?;
            Err(Error::from(e))
        }
    }
}
<|endoftext|>