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

use crate::associated_data_impl;
use crate::build::{
    build_constant_value, build_type_annotation, build_variable_definitions, ValidationLevel,
};
use crate::constants::ARGUMENT_DEFINITION;
use crate::errors::{ValidationMessage, ValidationMessageWithData};
use crate::ir::{ConstantValue, VariableDefinition};
use crate::GraphQLSuggestions;
use common::{Diagnostic, DiagnosticsResult, FeatureFlag, Location, WithLocation};
use errors::{par_try_map, try2};
use fnv::FnvHashMap;
use interner::{Intern, StringKey};
use lazy_static::lazy_static;
use schema::{SDLSchema, Schema, Type, TypeReference};

lazy_static! {
    static ref TYPE: StringKey = "type".intern();
    static ref DEFAULT_VALUE: StringKey = "defaultValue".intern();
    pub static ref PROVIDER_MODULE: StringKey = "provider".intern();
    pub static ref UNUSED_LOCAL_VARIABLE_DEPRECATED: StringKey =
        "unusedLocalVariable_DEPRECATED".intern();
}

pub type FragmentSignatures = FnvHashMap<StringKey, FragmentSignature>;

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
struct ProvidedArgument {
    module_name: StringKey,
}

associated_data_impl!(ProvidedArgument);

/// Describes the public API of a fragment, excluding its selections.
/// When translating ASTs to IR, fragment spread arguments must be
/// checked against the corresponding fragment definition. However,
/// this process can't use the IR fragment definition, since that
/// would depend on having checked its body! Since recursive fragments
/// are allowed, we break the cycle by first computing signatures
/// and using these to type check fragment spreads in selections.
#[derive(Debug, Eq, PartialEq)]
pub struct FragmentSignature {
    pub name: WithLocation<StringKey>,
    pub variable_definitions: Vec<VariableDefinition>,
    pub type_condition: Type,
}

pub fn build_signatures(
    schema: &SDLSchema,
    definitions: &[graphql_syntax::ExecutableDefinition],
    enable_provided_variables: &FeatureFlag,
) -> DiagnosticsResult<FragmentSignatures> {
    let suggestions = GraphQLSuggestions::new(schema);
    let mut seen_signatures: FnvHashMap<StringKey, FragmentSignature> =
        FnvHashMap::with_capacity_and_hasher(definitions.len(), Default::default());
    let signatures = par_try_map(definitions, |definition| match definition {
        graphql_syntax::ExecutableDefinition::Fragment(fragment) => Ok(Some(
            build_fragment_signature(schema, fragment, &suggestions, enable_provided_variables)?,
        )),
        graphql_syntax::ExecutableDefinition::Operation(_) => Ok(None),
    })?;
    let mut errors = Vec::new();
    for signature in signatures.into_iter().flatten() {
        let previous_signature = seen_signatures.get(&signature.name.item);
        if let Some(previous_signature) = previous_signature {
            errors.push(
                Diagnostic::error(
                    ValidationMessage::DuplicateDefinition(signature.name.item),
                    previous_signature.name.location,
                )
                .annotate("also defined here", signature.name.location),
            );
            continue;
        }
        seen_signatures.insert(signature.name.item, signature);
    }
    if errors.is_empty() {
        Ok(seen_signatures)
    } else {
        Err(errors)
    }
}

fn build_fragment_signature(
    schema: &SDLSchema,
    fragment: &graphql_syntax::FragmentDefinition,
    suggestions: &GraphQLSuggestions<'_>,
    enable_provided_variables: &FeatureFlag,
) -> DiagnosticsResult<FragmentSignature> {
    let type_name = fragment.type_condition.type_.value;
    let type_condition = match schema.get_type(type_name) {
        Some(type_condition) => match type_condition {
            Type::Interface(..) | Type::Object(..) | Type::Union(..) => Ok(type_condition),
            _ => Err(Diagnostic::error(
                ValidationMessage::ExpectedCompositeType(type_condition),
                fragment
                    .location
                    .with_span(fragment.type_condition.type_.span),
            )
            .into()),
        },
        None => Err(Diagnostic::error_with_data(
            ValidationMessageWithData::UnknownType {
                type_name,
                suggestions: suggestions.composite_type_suggestions(type_name),
            },
            fragment
                .location
                .with_span(fragment.type_condition.type_.span),
        )
        .into()),
    };
    let argument_definition_directives = fragment
        .directives
        .iter()
        .filter(|x| x.name.value == *ARGUMENT_DEFINITION)
        .collect::<Vec<_>>();
    if fragment.variable_definitions.is_some() && !argument_definition_directives.is_empty() {
        return Err(Diagnostic::error(
            ValidationMessage::VariableDefinitionsAndArgumentDirective(),
            fragment
                .location
                .with_span(argument_definition_directives[0].span),
        )
        .annotate(
            "variables are previously defined here",
            fragment.location.with_span(
                fragment
                    .variable_definitions
                    .as_ref()
                    .map(|list| list.span)
                    .unwrap(),
            ),
        )
        .into());
    } else if argument_definition_directives.len() > 1 {
        return Err(Diagnostic::error(
            ValidationMessage::ExpectedOneArgumentDefinitionsDirective(),
            fragment
                .location
                .with_span(argument_definition_directives[1].span),
        )
        .into());
    }
    let variable_definitions = fragment
        .variable_definitions
        .as_ref()
        .map(|variable_definitions| {
            build_variable_definitions(schema, &variable_definitions.items, fragment.location)
        })
        .or_else(|| {
            argument_definition_directives.get(0).map(|x| {
                build_fragment_variable_definitions(schema, fragment, x, enable_provided_variables)
            })
        })
        .unwrap_or_else(|| Ok(Default::default()));

    let (type_condition, variable_definitions) = try2(type_condition, variable_definitions)?;
    Ok(FragmentSignature {
        name: fragment
            .name
            .name_with_location(fragment.location.source_location()),
        type_condition,
        variable_definitions,
    })
}

fn build_fragment_variable_definitions(
    schema: &SDLSchema,
    fragment: &graphql_syntax::FragmentDefinition,
    directive: &graphql_syntax::Directive,
    enable_provided_variables: &FeatureFlag,
) -> DiagnosticsResult<Vec<VariableDefinition>> {
    if let Some(arguments) = &directive.arguments {
        Ok(arguments
            .items
            .iter()
            .map(|variable_arg| {
                if let graphql_syntax::Value::Constant(graphql_syntax::ConstantValue::Object(
                    object,
                )) = &variable_arg.value
                {
                    let variable_name = &variable_arg.name;
                    let mut extra_items = Vec::new();
                    let mut type_arg = None;
                    let mut default_arg = None;
                    let mut unused_local_variable_arg = None;
                    let mut provider_arg = None;
                    for item in &object.items {
                        let name = item.name.value;
                        if name == *TYPE {
                            type_arg = Some(item);
                        } else if name == *DEFAULT_VALUE {
                            default_arg = Some(item);
                        } else if name == *UNUSED_LOCAL_VARIABLE_DEPRECATED {
                            unused_local_variable_arg = Some(item);
                        } else if name == *PROVIDER_MODULE {
                            if !enable_provided_variables.is_enabled_for(variable_name.value) {
                                return Err(vec![Diagnostic::error(
                                    format!("Invalid usage of provided variable: this feature is gated and currently set to {}",
                                                    enable_provided_variables),
                                     fragment.location.with_span(item.span),
                                )]);
                            }
                            provider_arg = Some(item);
                        } else {
                            extra_items.push(item);
                        }
                    }
                    // Check that no extraneous keys were supplied
                    if !extra_items.is_empty() {
                        return Err(extra_items
                            .iter()
                            .map(|item| {
                                Diagnostic::error(
                                    ValidationMessage::InvalidArgumentDefinitionsKey(
                                        item.name.value,
                                    ),
                                    fragment.location.with_span(item.span),
                                )
                            })
                            .collect());
                    }

                    // Convert variable type, validate that it's an input type
                    let type_ = get_argument_type(schema, fragment.location, type_arg, object)?;
                    if !type_.inner().is_input_type() {
                        return Err(Diagnostic::error(
                            ValidationMessage::ExpectedFragmentArgumentToHaveInputType(
                                schema.get_type_name(type_.inner()),
                            ),
                            fragment.location.with_span(variable_arg.value.span()),
                        )
                        .into());
                    }

                    let mut directives = Vec::new();
                        if let Some(unused_local_variable_arg) = unused_local_variable_arg {
                            if !matches!(
                                unused_local_variable_arg,
                                graphql_syntax::ConstantArgument {
                                    value: graphql_syntax::ConstantValue::Boolean(
                                        graphql_syntax::BooleanNode { value: true, .. }
                                    ),
                                    ..
                                }
                            ) {
                                return Err(vec![Diagnostic::error(
                                    ValidationMessage::InvalidUnusedFragmentVariableSuppressionArg,
                                    fragment
                                        .location
                                        .with_span(unused_local_variable_arg.value.span()),
                                )]);
                            }
                            directives.push(crate::Directive {
                                name: WithLocation::new(
                                    fragment.location.with_span(unused_local_variable_arg.span),
                                    *UNUSED_LOCAL_VARIABLE_DEPRECATED,
                                ),
                                arguments: Vec::new(),
                                data: None,
                            });
                        }
                        if let Some(provider_arg) = provider_arg {
                            if let graphql_syntax::ConstantValue::String(provider_module_name) = &provider_arg.value {
                                if let Some(default_arg_) = default_arg {
                                    return Err(vec![Diagnostic::error(
                                        ValidationMessage::ProvidedVariableIncompatibleWithDefaultValue{argument_name: variable_name.value},
                                        fragment
                                            .location
                                            .with_span(provider_arg.span),
                                    ).annotate("Default value declared here",
                                    fragment
                                    .location
                                    .with_span(default_arg_.span))]);
                                }

                                directives.push(crate::Directive {
                                    name: WithLocation::new(
                                        fragment.location.with_span(provider_arg.span),
                                        *PROVIDER_MODULE,
                                    ),
                                    arguments: Vec::new(),
                                    data: Some(Box::new(ProvidedArgument{module_name: provider_module_name.value})),
                                });

                            } else{
                                return Err(vec![Diagnostic::error(
                                    ValidationMessage::LiteralStringArgumentExpectedForDirective{arg_name: *PROVIDER_MODULE, directive_name: *ARGUMENT_DEFINITION },
                                    fragment
                                        .location
                                        .with_span(provider_arg.value.span()),
                                )]);
                            }

                        }

                    let default_value =
                        get_default_value(schema, fragment.location, default_arg, &type_)?;

                    Ok(VariableDefinition {
                        name: variable_name
                            .name_with_location(fragment.location.source_location()),
                        type_,
                        directives,
                        default_value,
                    })
                } else {
                    Err(Diagnostic::error(
                        ValidationMessage::ExpectedArgumentDefinitionToBeObject(),
                        fragment.location.with_span(variable_arg.value.span()),
                    )
                    .into())
                }
            })
            .collect::<DiagnosticsResult<Vec<VariableDefinition>>>()?)
    } else {
        Ok(Default::default())
    }
}

fn get_argument_type(
    schema: &SDLSchema,
    location: Location,
    type_arg: Option<&graphql_syntax::ConstantArgument>,
    object: &graphql_syntax::List<graphql_syntax::ConstantArgument>,
) -> DiagnosticsResult<TypeReference> {
    let type_name_and_span = match type_arg {
        Some(graphql_syntax::ConstantArgument {
            value: graphql_syntax::ConstantValue::String(type_name_node),
            span,
            ..
        }) => Some((type_name_node.value, span)),
        Some(graphql_syntax::ConstantArgument {
            value: graphql_syntax::ConstantValue::Enum(type_name_node),
            span,
            ..
        }) => Some((type_name_node.value, span)),
        _ => None,
    };
    if let Some((type_name, &span)) = type_name_and_span {
        let type_ast = graphql_syntax::parse_type(type_name.lookup(), location.source_location())
            .map_err(|diagnostics| {
            diagnostics
                .into_iter()
                .map(|diagnostic| {
                    let message = diagnostic.message().to_string();
                    Diagnostic::error(
                        message,
                        // TODO: ideally, `parse_type()` would take in the offset
                        // location and report the error at the right location.
                        location.with_span(span),
                    )
                })
                .collect::<Vec<_>>()
        })?;
        let type_ = build_type_annotation(schema, &type_ast, location)?;
        Ok(type_)
    } else {
        Err(Diagnostic::error(
            ValidationMessage::ExpectedArgumentDefinitionLiteralType(),
            location.with_span(type_arg.map_or(object.span, |x| x.span)),
        )
        .into())
    }
}

fn get_default_value(
    schema: &SDLSchema,
    location: Location,
    default_arg: Option<&graphql_syntax::ConstantArgument>,
    type_: &TypeReference,
) -> DiagnosticsResult<Option<WithLocation<ConstantValue>>> {
    default_arg
        .map(|x| {
            let constant_value_span = x.value.span();
            build_constant_value(schema, &x.value, &type_, location, ValidationLevel::Strict).map(
                |constant_value| {
                    WithLocation::from_span(
                        location.source_location(),
                        constant_value_span,
                        constant_value,
                    )
                },
            )
        })
        .transpose()
}
<commit_msg>patch: feature should be gated by fragment 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.
 */

use crate::associated_data_impl;
use crate::build::{
    build_constant_value, build_type_annotation, build_variable_definitions, ValidationLevel,
};
use crate::constants::ARGUMENT_DEFINITION;
use crate::errors::{ValidationMessage, ValidationMessageWithData};
use crate::ir::{ConstantValue, VariableDefinition};
use crate::GraphQLSuggestions;
use common::{Diagnostic, DiagnosticsResult, FeatureFlag, Location, WithLocation};
use errors::{par_try_map, try2};
use fnv::FnvHashMap;
use interner::{Intern, StringKey};
use lazy_static::lazy_static;
use schema::{SDLSchema, Schema, Type, TypeReference};

lazy_static! {
    static ref TYPE: StringKey = "type".intern();
    static ref DEFAULT_VALUE: StringKey = "defaultValue".intern();
    pub static ref PROVIDER_MODULE: StringKey = "provider".intern();
    pub static ref UNUSED_LOCAL_VARIABLE_DEPRECATED: StringKey =
        "unusedLocalVariable_DEPRECATED".intern();
}

pub type FragmentSignatures = FnvHashMap<StringKey, FragmentSignature>;

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
struct ProvidedArgument {
    module_name: StringKey,
}

associated_data_impl!(ProvidedArgument);

/// Describes the public API of a fragment, excluding its selections.
/// When translating ASTs to IR, fragment spread arguments must be
/// checked against the corresponding fragment definition. However,
/// this process can't use the IR fragment definition, since that
/// would depend on having checked its body! Since recursive fragments
/// are allowed, we break the cycle by first computing signatures
/// and using these to type check fragment spreads in selections.
#[derive(Debug, Eq, PartialEq)]
pub struct FragmentSignature {
    pub name: WithLocation<StringKey>,
    pub variable_definitions: Vec<VariableDefinition>,
    pub type_condition: Type,
}

pub fn build_signatures(
    schema: &SDLSchema,
    definitions: &[graphql_syntax::ExecutableDefinition],
    enable_provided_variables: &FeatureFlag,
) -> DiagnosticsResult<FragmentSignatures> {
    let suggestions = GraphQLSuggestions::new(schema);
    let mut seen_signatures: FnvHashMap<StringKey, FragmentSignature> =
        FnvHashMap::with_capacity_and_hasher(definitions.len(), Default::default());
    let signatures = par_try_map(definitions, |definition| match definition {
        graphql_syntax::ExecutableDefinition::Fragment(fragment) => Ok(Some(
            build_fragment_signature(schema, fragment, &suggestions, enable_provided_variables)?,
        )),
        graphql_syntax::ExecutableDefinition::Operation(_) => Ok(None),
    })?;
    let mut errors = Vec::new();
    for signature in signatures.into_iter().flatten() {
        let previous_signature = seen_signatures.get(&signature.name.item);
        if let Some(previous_signature) = previous_signature {
            errors.push(
                Diagnostic::error(
                    ValidationMessage::DuplicateDefinition(signature.name.item),
                    previous_signature.name.location,
                )
                .annotate("also defined here", signature.name.location),
            );
            continue;
        }
        seen_signatures.insert(signature.name.item, signature);
    }
    if errors.is_empty() {
        Ok(seen_signatures)
    } else {
        Err(errors)
    }
}

fn build_fragment_signature(
    schema: &SDLSchema,
    fragment: &graphql_syntax::FragmentDefinition,
    suggestions: &GraphQLSuggestions<'_>,
    enable_provided_variables: &FeatureFlag,
) -> DiagnosticsResult<FragmentSignature> {
    let type_name = fragment.type_condition.type_.value;
    let type_condition = match schema.get_type(type_name) {
        Some(type_condition) => match type_condition {
            Type::Interface(..) | Type::Object(..) | Type::Union(..) => Ok(type_condition),
            _ => Err(Diagnostic::error(
                ValidationMessage::ExpectedCompositeType(type_condition),
                fragment
                    .location
                    .with_span(fragment.type_condition.type_.span),
            )
            .into()),
        },
        None => Err(Diagnostic::error_with_data(
            ValidationMessageWithData::UnknownType {
                type_name,
                suggestions: suggestions.composite_type_suggestions(type_name),
            },
            fragment
                .location
                .with_span(fragment.type_condition.type_.span),
        )
        .into()),
    };
    let argument_definition_directives = fragment
        .directives
        .iter()
        .filter(|x| x.name.value == *ARGUMENT_DEFINITION)
        .collect::<Vec<_>>();
    if fragment.variable_definitions.is_some() && !argument_definition_directives.is_empty() {
        return Err(Diagnostic::error(
            ValidationMessage::VariableDefinitionsAndArgumentDirective(),
            fragment
                .location
                .with_span(argument_definition_directives[0].span),
        )
        .annotate(
            "variables are previously defined here",
            fragment.location.with_span(
                fragment
                    .variable_definitions
                    .as_ref()
                    .map(|list| list.span)
                    .unwrap(),
            ),
        )
        .into());
    } else if argument_definition_directives.len() > 1 {
        return Err(Diagnostic::error(
            ValidationMessage::ExpectedOneArgumentDefinitionsDirective(),
            fragment
                .location
                .with_span(argument_definition_directives[1].span),
        )
        .into());
    }
    let variable_definitions = fragment
        .variable_definitions
        .as_ref()
        .map(|variable_definitions| {
            build_variable_definitions(schema, &variable_definitions.items, fragment.location)
        })
        .or_else(|| {
            argument_definition_directives.get(0).map(|x| {
                build_fragment_variable_definitions(schema, fragment, x, enable_provided_variables)
            })
        })
        .unwrap_or_else(|| Ok(Default::default()));

    let (type_condition, variable_definitions) = try2(type_condition, variable_definitions)?;
    Ok(FragmentSignature {
        name: fragment
            .name
            .name_with_location(fragment.location.source_location()),
        type_condition,
        variable_definitions,
    })
}

fn build_fragment_variable_definitions(
    schema: &SDLSchema,
    fragment: &graphql_syntax::FragmentDefinition,
    directive: &graphql_syntax::Directive,
    enable_provided_variables: &FeatureFlag,
) -> DiagnosticsResult<Vec<VariableDefinition>> {
    if let Some(arguments) = &directive.arguments {
        Ok(arguments
            .items
            .iter()
            .map(|variable_arg| {
                if let graphql_syntax::Value::Constant(graphql_syntax::ConstantValue::Object(
                    object,
                )) = &variable_arg.value
                {
                    let variable_name = &variable_arg.name;
                    let mut extra_items = Vec::new();
                    let mut type_arg = None;
                    let mut default_arg = None;
                    let mut unused_local_variable_arg = None;
                    let mut provider_arg = None;
                    for item in &object.items {
                        let name = item.name.value;
                        if name == *TYPE {
                            type_arg = Some(item);
                        } else if name == *DEFAULT_VALUE {
                            default_arg = Some(item);
                        } else if name == *UNUSED_LOCAL_VARIABLE_DEPRECATED {
                            unused_local_variable_arg = Some(item);
                        } else if name == *PROVIDER_MODULE {
                            if !enable_provided_variables.is_enabled_for(fragment.name.value) {
                                return Err(vec![Diagnostic::error(
                                    format!("Invalid usage of provided variable: this feature is gated and currently set to {}",
                                                    enable_provided_variables),
                                     fragment.location.with_span(item.span),
                                )]);
                            }
                            provider_arg = Some(item);
                        } else {
                            extra_items.push(item);
                        }
                    }
                    // Check that no extraneous keys were supplied
                    if !extra_items.is_empty() {
                        return Err(extra_items
                            .iter()
                            .map(|item| {
                                Diagnostic::error(
                                    ValidationMessage::InvalidArgumentDefinitionsKey(
                                        item.name.value,
                                    ),
                                    fragment.location.with_span(item.span),
                                )
                            })
                            .collect());
                    }

                    // Convert variable type, validate that it's an input type
                    let type_ = get_argument_type(schema, fragment.location, type_arg, object)?;
                    if !type_.inner().is_input_type() {
                        return Err(Diagnostic::error(
                            ValidationMessage::ExpectedFragmentArgumentToHaveInputType(
                                schema.get_type_name(type_.inner()),
                            ),
                            fragment.location.with_span(variable_arg.value.span()),
                        )
                        .into());
                    }

                    let mut directives = Vec::new();
                        if let Some(unused_local_variable_arg) = unused_local_variable_arg {
                            if !matches!(
                                unused_local_variable_arg,
                                graphql_syntax::ConstantArgument {
                                    value: graphql_syntax::ConstantValue::Boolean(
                                        graphql_syntax::BooleanNode { value: true, .. }
                                    ),
                                    ..
                                }
                            ) {
                                return Err(vec![Diagnostic::error(
                                    ValidationMessage::InvalidUnusedFragmentVariableSuppressionArg,
                                    fragment
                                        .location
                                        .with_span(unused_local_variable_arg.value.span()),
                                )]);
                            }
                            directives.push(crate::Directive {
                                name: WithLocation::new(
                                    fragment.location.with_span(unused_local_variable_arg.span),
                                    *UNUSED_LOCAL_VARIABLE_DEPRECATED,
                                ),
                                arguments: Vec::new(),
                                data: None,
                            });
                        }
                        if let Some(provider_arg) = provider_arg {
                            if let graphql_syntax::ConstantValue::String(provider_module_name) = &provider_arg.value {
                                if let Some(default_arg_) = default_arg {
                                    return Err(vec![Diagnostic::error(
                                        ValidationMessage::ProvidedVariableIncompatibleWithDefaultValue{argument_name: variable_name.value},
                                        fragment
                                            .location
                                            .with_span(provider_arg.span),
                                    ).annotate("Default value declared here",
                                    fragment
                                    .location
                                    .with_span(default_arg_.span))]);
                                }

                                directives.push(crate::Directive {
                                    name: WithLocation::new(
                                        fragment.location.with_span(provider_arg.span),
                                        *PROVIDER_MODULE,
                                    ),
                                    arguments: Vec::new(),
                                    data: Some(Box::new(ProvidedArgument{module_name: provider_module_name.value})),
                                });

                            } else{
                                return Err(vec![Diagnostic::error(
                                    ValidationMessage::LiteralStringArgumentExpectedForDirective{arg_name: *PROVIDER_MODULE, directive_name: *ARGUMENT_DEFINITION },
                                    fragment
                                        .location
                                        .with_span(provider_arg.value.span()),
                                )]);
                            }

                        }

                    let default_value =
                        get_default_value(schema, fragment.location, default_arg, &type_)?;

                    Ok(VariableDefinition {
                        name: variable_name
                            .name_with_location(fragment.location.source_location()),
                        type_,
                        directives,
                        default_value,
                    })
                } else {
                    Err(Diagnostic::error(
                        ValidationMessage::ExpectedArgumentDefinitionToBeObject(),
                        fragment.location.with_span(variable_arg.value.span()),
                    )
                    .into())
                }
            })
            .collect::<DiagnosticsResult<Vec<VariableDefinition>>>()?)
    } else {
        Ok(Default::default())
    }
}

fn get_argument_type(
    schema: &SDLSchema,
    location: Location,
    type_arg: Option<&graphql_syntax::ConstantArgument>,
    object: &graphql_syntax::List<graphql_syntax::ConstantArgument>,
) -> DiagnosticsResult<TypeReference> {
    let type_name_and_span = match type_arg {
        Some(graphql_syntax::ConstantArgument {
            value: graphql_syntax::ConstantValue::String(type_name_node),
            span,
            ..
        }) => Some((type_name_node.value, span)),
        Some(graphql_syntax::ConstantArgument {
            value: graphql_syntax::ConstantValue::Enum(type_name_node),
            span,
            ..
        }) => Some((type_name_node.value, span)),
        _ => None,
    };
    if let Some((type_name, &span)) = type_name_and_span {
        let type_ast = graphql_syntax::parse_type(type_name.lookup(), location.source_location())
            .map_err(|diagnostics| {
            diagnostics
                .into_iter()
                .map(|diagnostic| {
                    let message = diagnostic.message().to_string();
                    Diagnostic::error(
                        message,
                        // TODO: ideally, `parse_type()` would take in the offset
                        // location and report the error at the right location.
                        location.with_span(span),
                    )
                })
                .collect::<Vec<_>>()
        })?;
        let type_ = build_type_annotation(schema, &type_ast, location)?;
        Ok(type_)
    } else {
        Err(Diagnostic::error(
            ValidationMessage::ExpectedArgumentDefinitionLiteralType(),
            location.with_span(type_arg.map_or(object.span, |x| x.span)),
        )
        .into())
    }
}

fn get_default_value(
    schema: &SDLSchema,
    location: Location,
    default_arg: Option<&graphql_syntax::ConstantArgument>,
    type_: &TypeReference,
) -> DiagnosticsResult<Option<WithLocation<ConstantValue>>> {
    default_arg
        .map(|x| {
            let constant_value_span = x.value.span();
            build_constant_value(schema, &x.value, &type_, location, ValidationLevel::Strict).map(
                |constant_value| {
                    WithLocation::from_span(
                        location.source_location(),
                        constant_value_span,
                        constant_value,
                    )
                },
            )
        })
        .transpose()
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate clap;
extern crate env_logger;
extern crate habitat_core as hcore;
extern crate habitat_common as common;
extern crate habitat_pkg_export_docker as export_docker;
extern crate handlebars;
extern crate rand;
#[macro_use]
extern crate serde_json;
#[macro_use]
extern crate log;

extern crate failure;
#[macro_use]
extern crate failure_derive;

mod topology;
mod error;
mod manifest;
mod cli;

use hcore::PROGRAM_NAME;
use common::ui::UI;

use export_docker::Result;

use manifest::Manifest;

// Synced with the version of the Habitat operator.
pub const VERSION: &'static str = "0.1.0";

fn main() {
    env_logger::init().unwrap();
    let mut ui = UI::default_with_env();
    let m = cli().get_matches();
    debug!("clap cli args: {:?}", m);

    if let Err(e) = start(&mut ui, &m) {
        let _ = ui.fatal(e);
        std::process::exit(1)
    }
}

fn start(ui: &mut UI, matches: &clap::ArgMatches) -> Result<()> {
    if !matches.is_present("NO_DOCKER_IMAGE") {
        export_docker::export_for_cli_matches(ui, &matches)?;
    }
    let mut manifest = Manifest::new_from_cli_matches(ui, &matches)?;
    manifest.generate()
}

fn cli<'a, 'b>() -> clap::App<'a, 'b> {
    let name: &str = &*PROGRAM_NAME;
    let about = "Creates a Docker image and Kubernetes manifest for a Habitat package. Habitat \
                 operator must be deployed within the Kubernetes cluster before the generated \
                 manifest can be applied to this cluster.";

    cli::Cli::new(name, about).add_all_args().app
}
<commit_msg>pkg-export-k8s: Rename exporting function<commit_after>// Copyright (c) 2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate clap;
extern crate env_logger;
extern crate habitat_core as hcore;
extern crate habitat_common as common;
extern crate habitat_pkg_export_docker as export_docker;
extern crate handlebars;
extern crate rand;
#[macro_use]
extern crate serde_json;
#[macro_use]
extern crate log;

extern crate failure;
#[macro_use]
extern crate failure_derive;

mod topology;
mod error;
mod manifest;
mod cli;

use hcore::PROGRAM_NAME;
use common::ui::UI;

use export_docker::Result;

use manifest::Manifest;

// Synced with the version of the Habitat operator.
pub const VERSION: &'static str = "0.1.0";

fn main() {
    env_logger::init().unwrap();
    let mut ui = UI::default_with_env();
    let m = cli().get_matches();
    debug!("clap cli args: {:?}", m);

    if let Err(e) = export_for_cli_matches(&mut ui, &m) {
        let _ = ui.fatal(e);
        std::process::exit(1)
    }
}

fn export_for_cli_matches(ui: &mut UI, matches: &clap::ArgMatches) -> Result<()> {
    if !matches.is_present("NO_DOCKER_IMAGE") {
        export_docker::export_for_cli_matches(ui, &matches)?;
    }
    let mut manifest = Manifest::new_from_cli_matches(ui, &matches)?;
    manifest.generate()
}

fn cli<'a, 'b>() -> clap::App<'a, 'b> {
    let name: &str = &*PROGRAM_NAME;
    let about = "Creates a Docker image and Kubernetes manifest for a Habitat package. Habitat \
                 operator must be deployed within the Kubernetes cluster before the generated \
                 manifest can be applied to this cluster.";

    cli::Cli::new(name, about).add_all_args().app
}
<|endoftext|>


<commit_before>use std::{
    cell::{Cell, RefCell},
    collections::BTreeMap,
    rc::Rc,
};

use dces::prelude::{Entity, EntityComponentManager, System};

use crate::{css_engine::*, prelude::*, shell::WindowShell, tree::Tree, utils::*};

/// The `EventStateSystem` pops events from the event queue and delegates the events to the corresponding event handlers of the widgets and updates the states.
pub struct EventStateSystem {
    pub shell: Rc<RefCell<WindowShell<WindowAdapter>>>,
    pub handlers: EventHandlerMap,
    pub update: Rc<Cell<bool>>,
    pub running: Rc<Cell<bool>>,
    pub mouse_down_nodes: RefCell<Vec<Entity>>,
    pub states: Rc<RefCell<BTreeMap<Entity, Rc<dyn State>>>>,
    pub render_objects: Rc<RefCell<BTreeMap<Entity, Box<dyn RenderObject>>>>,
    pub layouts: Rc<RefCell<BTreeMap<Entity, Box<dyn Layout>>>>,
}

impl EventStateSystem {
    fn process_top_down_event(
        &self,
        _event: &EventBox,
        _ecm: &mut EntityComponentManager<Tree, StringComponentStore>,
    ) {
    }

    fn process_bottom_up_event(
        &self,
        mouse_position: Point,
        event: &EventBox,
        ecm: &mut EntityComponentManager<Tree, StringComponentStore>,
    ) {
        let mut matching_nodes = vec![];

        let mut current_node = event.source;
        let root = ecm.entity_store().root;

        let theme = ecm
            .component_store()
            .get::<Theme>("theme", root)
            .unwrap()
            .clone();

        // resize
        if let Ok(WindowEvent::Resize { width, height }) = event.downcast_ref::<WindowEvent>() {
            // update window size
            if let Ok(bounds) = ecm
                .component_store_mut()
                .get_mut::<Rectangle>("bounds", root)
            {
                bounds.set_width(*width);
                bounds.set_height(*height);
            }

            if let Ok(constraint) = ecm
                .component_store_mut()
                .get_mut::<Constraint>("constraint", root)
            {
                constraint.set_width(*width);
                constraint.set_height(*height);
            }

            self.update.set(true);
        }

        // global key handling
        if let Ok(event) = event.downcast_ref::<KeyDownEvent>() {
            if let Ok(global) = ecm.component_store_mut().get_mut::<Global>("global", root) {
                // Set this value on the keyboard state
                global.keyboard_state.set_key_state(event.event.key, true);
            }
        }

        if let Ok(event) = event.downcast_ref::<KeyUpEvent>() {
            if let Ok(global) = ecm.component_store_mut().get_mut::<Global>("global", root) {
                // Set this value on the keyboard state
                global.keyboard_state.set_key_state(event.event.key, false);
            }
        }

        let mut unknown_event = true;
        let mut clipped_parent = vec![];

        loop {
            if let Some(cp) = clipped_parent.last() {
                if ecm.entity_store().parent[&current_node] == Some(*cp) {
                    clipped_parent.push(current_node);
                } else {
                    clipped_parent.pop();
                }
            }

            // key down event
            if event.downcast_ref::<KeyDownEvent>().is_ok() {
                if let Some(focused) = ecm
                    .component_store()
                    .get::<Global>("global", root)
                    .unwrap()
                    .focused_widget
                {
                    if current_node == focused {
                        matching_nodes.push(current_node);
                    }
                }

                unknown_event = false;
            }

            // key up event
            if event.downcast_ref::<KeyUpEvent>().is_ok() {
                if let Some(focused) = ecm
                    .component_store()
                    .get::<Global>("global", root)
                    .unwrap()
                    .focused_widget
                {
                    if current_node == focused {
                        matching_nodes.push(current_node);
                    }
                }

                unknown_event = false;
            }

            // scroll handling
            if event.downcast_ref::<ScrollEvent>().is_ok() {
                if check_mouse_condition(
                    mouse_position,
                    &WidgetContainer::new(current_node, ecm, &theme),
                ) {
                    matching_nodes.push(current_node);
                }

                unknown_event = false;
            }

            // click handling
            if let Ok(event) = event.downcast_ref::<ClickEvent>() {
                if check_mouse_condition(
                    event.position,
                    &WidgetContainer::new(current_node, ecm, &theme),
                ) {
                    let mut add = true;
                    if let Some(op) = clipped_parent.get(0) {
                        if !check_mouse_condition(
                            event.position,
                            &WidgetContainer::new(*op, ecm, &theme),
                        ) {
                            add = false;
                        }
                    }

                    if add {
                        matching_nodes.push(current_node);
                        self.mouse_down_nodes.borrow_mut().push(current_node);
                    }
                }

                unknown_event = false;
            }

            // mouse down handling
            if let Ok(event) = event.downcast_ref::<MouseDownEvent>() {
                if check_mouse_condition(
                    Point::new(event.x, event.y),
                    &WidgetContainer::new(current_node, ecm, &theme),
                ) {
                    let mut add = true;
                    if let Some(op) = clipped_parent.get(0) {
                        // todo: improve check path if exists
                        if !check_mouse_condition(
                            Point::new(event.x, event.y),
                            &WidgetContainer::new(*op, ecm, &theme),
                        ) {
                            add = false;
                        }
                    }

                    if add {
                        matching_nodes.push(current_node);
                        self.mouse_down_nodes.borrow_mut().push(current_node);
                    }
                }

                unknown_event = false;
            }

            // mouse up handling
            if event.downcast_ref::<MouseUpEvent>().is_ok() {
                if self.mouse_down_nodes.borrow().contains(&current_node) {
                    matching_nodes.push(current_node);
                    let index = self
                        .mouse_down_nodes
                        .borrow()
                        .iter()
                        .position(|x| *x == current_node)
                        .unwrap();
                    self.mouse_down_nodes.borrow_mut().remove(index);
                }

                unknown_event = false;
            }

            if unknown_event
                && *WidgetContainer::new(current_node, ecm, &theme).get::<bool>("enabled")
            {
                if let Some(handlers) = self.handlers.borrow().get(&current_node) {
                    if handlers.iter().any(|handler| handler.handles_event(&event)) {
                        matching_nodes.push(current_node);
                    }
                }
            }

            if let Ok(clip) = ecm.component_store().get::<bool>("clip", current_node) {
                if *clip {
                    clipped_parent.clear();
                    clipped_parent.push(current_node);
                }
            }

            let mut it = ecm.entity_store().start_node(current_node).into_iter();
            it.next();

            if let Some(node) = it.next() {
                current_node = node;
            } else {
                break;
            }
        }

        let mut handled = false;
        let mut disabled_parent = None;

        for node in matching_nodes.iter().rev() {
            if let Some(dp) = disabled_parent {
                if ecm.entity_store().parent[&node] == Some(dp) {
                    disabled_parent = Some(*node);
                    continue;
                } else {
                    disabled_parent = None;
                }
            }

            if let Ok(enabled) = ecm.component_store().get::<bool>("enabled", *node) {
                if !enabled {
                    disabled_parent = Some(*node);
                    continue;
                }
            }

            if let Some(handlers) = self.handlers.borrow().get(node) {
                handled = handlers.iter().any(|handler| handler.handle_event(event));

                self.update.set(true);
            }

            if handled {
                break;
            }
        }
    }
}

impl System<Tree, StringComponentStore> for EventStateSystem {
    fn run(&self, ecm: &mut EntityComponentManager<Tree, StringComponentStore>) {
        let mut shell = self.shell.borrow_mut();

        loop {
            {
                let adapter = shell.adapter();
                let mouse_position = adapter.mouse_position;
                for event in adapter.event_queue.into_iter() {
                    if let Ok(event) = event.downcast_ref::<SystemEvent>() {
                        match event {
                            SystemEvent::Quit => {
                                self.running.set(false);
                                return;
                            }
                        }
                    }

                    match event.strategy {
                        EventStrategy::TopDown => {
                            self.process_top_down_event(&event, ecm);
                        }
                        EventStrategy::BottomUp => {
                            self.process_bottom_up_event(mouse_position, &event, ecm);
                        }
                        _ => {}
                    }
                }
            }

            // handle states

            let root = ecm.entity_store().root;

            let theme = ecm
                .component_store()
                .get::<Theme>("theme", root)
                .unwrap()
                .clone();
            let mut current_node = root;

            loop {
                let mut skip = false;

                {
                    let mut ctx = Context::new(
                        (current_node, ecm),
                        &mut shell,
                        &theme,
                        self.render_objects.clone(),
                        self.layouts.clone(),
                        self.handlers.clone(),
                        self.states.clone(),
                    );

                    if !self.states.borrow().contains_key(&current_node) {
                        skip = true;
                    }

                    if !skip {
                        if let Some(state) = self.states.borrow().get(&current_node) {
                            state.update(&mut ctx);
                        }
                    }
                }
                let mut it = ecm.entity_store().start_node(current_node).into_iter();
                it.next();

                if let Some(node) = it.next() {
                    current_node = node;
                } else {
                    break;
                }
            }

            if shell.adapter().event_queue.is_empty() {
                break;
            }
        }
    }
}
<commit_msg>Mouse move event fix.<commit_after>use std::{
    cell::{Cell, RefCell},
    collections::BTreeMap,
    rc::Rc,
};

use dces::prelude::{Entity, EntityComponentManager, System};

use crate::{css_engine::*, prelude::*, shell::WindowShell, tree::Tree, utils::*};

/// The `EventStateSystem` pops events from the event queue and delegates the events to the corresponding event handlers of the widgets and updates the states.
pub struct EventStateSystem {
    pub shell: Rc<RefCell<WindowShell<WindowAdapter>>>,
    pub handlers: EventHandlerMap,
    pub update: Rc<Cell<bool>>,
    pub running: Rc<Cell<bool>>,
    pub mouse_down_nodes: RefCell<Vec<Entity>>,
    pub states: Rc<RefCell<BTreeMap<Entity, Rc<dyn State>>>>,
    pub render_objects: Rc<RefCell<BTreeMap<Entity, Box<dyn RenderObject>>>>,
    pub layouts: Rc<RefCell<BTreeMap<Entity, Box<dyn Layout>>>>,
}

impl EventStateSystem {
    fn process_top_down_event(
        &self,
        _event: &EventBox,
        _ecm: &mut EntityComponentManager<Tree, StringComponentStore>,
    ) {
    }

    fn process_bottom_up_event(
        &self,
        mouse_position: Point,
        event: &EventBox,
        ecm: &mut EntityComponentManager<Tree, StringComponentStore>,
    ) {
        let mut matching_nodes = vec![];

        let mut current_node = event.source;
        let root = ecm.entity_store().root;

        let theme = ecm
            .component_store()
            .get::<Theme>("theme", root)
            .unwrap()
            .clone();

        // resize
        if let Ok(WindowEvent::Resize { width, height }) = event.downcast_ref::<WindowEvent>() {
            // update window size
            if let Ok(bounds) = ecm
                .component_store_mut()
                .get_mut::<Rectangle>("bounds", root)
            {
                bounds.set_width(*width);
                bounds.set_height(*height);
            }

            if let Ok(constraint) = ecm
                .component_store_mut()
                .get_mut::<Constraint>("constraint", root)
            {
                constraint.set_width(*width);
                constraint.set_height(*height);
            }

            self.update.set(true);
        }

        // global key handling
        if let Ok(event) = event.downcast_ref::<KeyDownEvent>() {
            if let Ok(global) = ecm.component_store_mut().get_mut::<Global>("global", root) {
                // Set this value on the keyboard state
                global.keyboard_state.set_key_state(event.event.key, true);
            }
        }

        if let Ok(event) = event.downcast_ref::<KeyUpEvent>() {
            if let Ok(global) = ecm.component_store_mut().get_mut::<Global>("global", root) {
                // Set this value on the keyboard state
                global.keyboard_state.set_key_state(event.event.key, false);
            }
        }

        let mut unknown_event = true;
        let mut clipped_parent = vec![];

        loop {
            if let Some(cp) = clipped_parent.last() {
                if ecm.entity_store().parent[&current_node] == Some(*cp) {
                    clipped_parent.push(current_node);
                } else {
                    clipped_parent.pop();
                }
            }

            // key down event
            if event.downcast_ref::<KeyDownEvent>().is_ok() {
                if let Some(focused) = ecm
                    .component_store()
                    .get::<Global>("global", root)
                    .unwrap()
                    .focused_widget
                {
                    if current_node == focused {
                        matching_nodes.push(current_node);
                    }
                }

                unknown_event = false;
            }

            // key up event
            if event.downcast_ref::<KeyUpEvent>().is_ok() {
                if let Some(focused) = ecm
                    .component_store()
                    .get::<Global>("global", root)
                    .unwrap()
                    .focused_widget
                {
                    if current_node == focused {
                        matching_nodes.push(current_node);
                    }
                }

                unknown_event = false;
            }

            // scroll handling
            if event.downcast_ref::<ScrollEvent>().is_ok() {
                if check_mouse_condition(
                    mouse_position,
                    &WidgetContainer::new(current_node, ecm, &theme),
                ) {
                    matching_nodes.push(current_node);
                }

                unknown_event = false;
            }

            // click handling
            if let Ok(event) = event.downcast_ref::<ClickEvent>() {
                if check_mouse_condition(
                    event.position,
                    &WidgetContainer::new(current_node, ecm, &theme),
                ) {
                    let mut add = true;
                    if let Some(op) = clipped_parent.get(0) {
                        if !check_mouse_condition(
                            event.position,
                            &WidgetContainer::new(*op, ecm, &theme),
                        ) {
                            add = false;
                        }
                    }

                    if add {
                        matching_nodes.push(current_node);
                        self.mouse_down_nodes.borrow_mut().push(current_node);
                    }
                }

                unknown_event = false;
            }

            // mouse down handling
            if let Ok(event) = event.downcast_ref::<MouseDownEvent>() {
                if check_mouse_condition(
                    Point::new(event.x, event.y),
                    &WidgetContainer::new(current_node, ecm, &theme),
                ) {
                    let mut add = true;
                    if let Some(op) = clipped_parent.get(0) {
                        // todo: improve check path if exists
                        if !check_mouse_condition(
                            Point::new(event.x, event.y),
                            &WidgetContainer::new(*op, ecm, &theme),
                        ) {
                            add = false;
                        }
                    }

                    if add {
                        matching_nodes.push(current_node);
                        self.mouse_down_nodes.borrow_mut().push(current_node);
                    }
                }

                unknown_event = false;
            }

             // mouse down handling
             if let Ok(event) = event.downcast_ref::<MouseMoveEvent>() {
                if check_mouse_condition(
                    Point::new(event.x, event.y),
                    &WidgetContainer::new(current_node, ecm, &theme),
                ) {
                    let mut add = true;
                    if let Some(op) = clipped_parent.get(0) {
                        // todo: improve check path if exists
                        if !check_mouse_condition(
                            Point::new(event.x, event.y),
                            &WidgetContainer::new(*op, ecm, &theme),
                        ) {
                            add = false;
                        }
                    }

                    if add {
                        matching_nodes.push(current_node);
                        self.mouse_down_nodes.borrow_mut().push(current_node);
                    }
                }

                unknown_event = false;
            }

            // mouse up handling
            if event.downcast_ref::<MouseUpEvent>().is_ok() {
                if self.mouse_down_nodes.borrow().contains(&current_node) {
                    matching_nodes.push(current_node);
                    let index = self
                        .mouse_down_nodes
                        .borrow()
                        .iter()
                        .position(|x| *x == current_node)
                        .unwrap();
                    self.mouse_down_nodes.borrow_mut().remove(index);
                }

                unknown_event = false;
            }

            if unknown_event
                && *WidgetContainer::new(current_node, ecm, &theme).get::<bool>("enabled")
            {
                if let Some(handlers) = self.handlers.borrow().get(&current_node) {
                    if handlers.iter().any(|handler| handler.handles_event(&event)) {
                        matching_nodes.push(current_node);
                    }
                }
            }

            if let Ok(clip) = ecm.component_store().get::<bool>("clip", current_node) {
                if *clip {
                    clipped_parent.clear();
                    clipped_parent.push(current_node);
                }
            }

            let mut it = ecm.entity_store().start_node(current_node).into_iter();
            it.next();

            if let Some(node) = it.next() {
                current_node = node;
            } else {
                break;
            }
        }

        let mut handled = false;
        let mut disabled_parent = None;

        for node in matching_nodes.iter().rev() {
            if let Some(dp) = disabled_parent {
                if ecm.entity_store().parent[&node] == Some(dp) {
                    disabled_parent = Some(*node);
                    continue;
                } else {
                    disabled_parent = None;
                }
            }

            if let Ok(enabled) = ecm.component_store().get::<bool>("enabled", *node) {
                if !enabled {
                    disabled_parent = Some(*node);
                    continue;
                }
            }

            if let Some(handlers) = self.handlers.borrow().get(node) {
                handled = handlers.iter().any(|handler| handler.handle_event(event));

                self.update.set(true);
            }

            if handled {
                break;
            }
        }
    }
}

impl System<Tree, StringComponentStore> for EventStateSystem {
    fn run(&self, ecm: &mut EntityComponentManager<Tree, StringComponentStore>) {
        let mut shell = self.shell.borrow_mut();

        loop {
            {
                let adapter = shell.adapter();
                let mouse_position = adapter.mouse_position;
                for event in adapter.event_queue.into_iter() {
                    if let Ok(event) = event.downcast_ref::<SystemEvent>() {
                        match event {
                            SystemEvent::Quit => {
                                self.running.set(false);
                                return;
                            }
                        }
                    }

                    match event.strategy {
                        EventStrategy::TopDown => {
                            self.process_top_down_event(&event, ecm);
                        }
                        EventStrategy::BottomUp => {
                            self.process_bottom_up_event(mouse_position, &event, ecm);
                        }
                        _ => {}
                    }
                }
            }

            // handle states

            let root = ecm.entity_store().root;

            let theme = ecm
                .component_store()
                .get::<Theme>("theme", root)
                .unwrap()
                .clone();
            let mut current_node = root;

            loop {
                let mut skip = false;

                {
                    let mut ctx = Context::new(
                        (current_node, ecm),
                        &mut shell,
                        &theme,
                        self.render_objects.clone(),
                        self.layouts.clone(),
                        self.handlers.clone(),
                        self.states.clone(),
                    );

                    if !self.states.borrow().contains_key(&current_node) {
                        skip = true;
                    }

                    if !skip {
                        if let Some(state) = self.states.borrow().get(&current_node) {
                            state.update(&mut ctx);
                        }
                    }
                }
                let mut it = ecm.entity_store().start_node(current_node).into_iter();
                it.next();

                if let Some(node) = it.next() {
                    current_node = node;
                } else {
                    break;
                }
            }

            if shell.adapter().event_queue.is_empty() {
                break;
            }
        }
    }
}
<|endoftext|>


<commit_before>
/*
0,3      3,3
#########
#S| | | #
#-#-#-#-#
# | | | #
#-#-#-#-#
# | | | #
#-#-#-#-#
# | | |
####### V
0,0     3,0
*/

// Move from (0,3) -> (3,0)

extern crate crypto;

mod hasher;

#[derive(Debug)]
enum Dir {
    Up, Down, Left, Right
}

impl Dir {
    fn from_val(val: usize) -> Dir {
        match val {
            0 => Dir::Up,
            1 => Dir::Down,
            2 => Dir::Left,
            3 => Dir::Right,
            _ => panic!("Unexpected direction code")
        }
    }
}

fn is_open(chr: char) -> bool {
    chr == 'b'
        || chr == 'c'
        || chr == 'd'
        || chr == 'e'
        || chr == 'f'
}

fn get_open_directions(code: &str) -> Vec<Dir> {
    let hash = hasher::hash(code);
    let directions = hash.chars()
        .take(4)
        .enumerate()
        .filter_map(|(idx, chr)|
            if is_open(chr) {
                Some(Dir::from_val(idx))
            } else {None})
        .collect::<Vec<_>>();
    //let first = hash.;
    directions
}

#[derive(Debug,Clone)]
struct State {
    pos: (i32, i32),
    state: String
}

impl State {
    fn new(code: &str) -> State {
        State{pos: (0,3), state: code.to_owned() }
    }

    fn is_valid(&self) -> bool {
        self.pos.0 >= 0 && self.pos.0 < 4 && self.pos.1 >= 0 && self.pos.1 < 4
    }

    fn is_target(&self) -> bool {
        self.pos == (3,0)
    }

    fn move_dir(&self, direction: &Dir) -> Option<State> {
        let next = match *direction {
            Dir::Up => State{pos: (self.pos.0, self.pos.1 + 1),
                state: self.state.to_owned() + "U"
            },
            Dir::Down => State{pos: (self.pos.0, self.pos.1 - 1),
                state: self.state.to_owned() + "D"
            },
            Dir::Left => State{pos: (self.pos.0 - 1, self.pos.1),
                state: self.state.to_owned() + "L"
            },
            Dir::Right => State{pos: (self.pos.0 + 1, self.pos.1),
                state: self.state.to_owned() + "R"
            },
        };

        if next.is_valid() { Some(next) } else {None}
    }
}

// Recursively search and find the target position
fn find_path(start: State) -> Option<State> {

    if start.is_target() {
        return Some(start);
    }

    //println!("Find path on {}", start.state);
    get_open_directions(&start.state)
        .iter()
        .filter_map(|dir| start.move_dir(dir))
        .filter_map(|next| find_path(next))
        .min_by_key(|state| state.state.len())
}

fn main() {
    {
        let example_start = State::new("hijkl");
        let min_path = find_path(example_start);
        //println!("{:?}", min_path);
        assert!(min_path.is_none());
    }

    {
        let example_start = State::new("ihgpwlah");
        let min_path = find_path(example_start);
        //println!("{:?}", min_path);
        assert_eq!(min_path.unwrap().state, "ihgpwlahDDRRRD");
    }

    {
        let example_start = State::new("kglvqrro");
        let min_path = find_path(example_start);
        //println!("{:?}", min_path);
        assert_eq!(min_path.unwrap().state, "kglvqrroDDUDRLRRUDRD");
    }

    {
        let example_start = State::new("ulqzkmiv");
        let min_path = find_path(example_start);
        //println!("{:?}", min_path);
        assert_eq!(min_path.unwrap().state, "ulqzkmivDRURDRUDDLLDLUURRDULRLDUUDDDRR");
    }

    // Part 1
    {
        let part1_input = "vwbaicqe";
        let example_start = State::new(part1_input);
        let min_path = find_path(example_start);
        println!("Part 1: {:?}", &min_path);
        assert_eq!(min_path.unwrap().state, "vwbaicqeDRDRULRDRD");
    }
}
<commit_msg>Day 17, part 2<commit_after>
/*
0,3      3,3
#########
#S| | | #
#-#-#-#-#
# | | | #
#-#-#-#-#
# | | | #
#-#-#-#-#
# | | |
####### V
0,0     3,0
*/

// Move from (0,3) -> (3,0)

extern crate crypto;

mod hasher;

#[derive(Debug)]
enum Dir {
    Up, Down, Left, Right
}

impl Dir {
    fn from_val(val: usize) -> Dir {
        match val {
            0 => Dir::Up,
            1 => Dir::Down,
            2 => Dir::Left,
            3 => Dir::Right,
            _ => panic!("Unexpected direction code")
        }
    }
}

fn is_open(chr: char) -> bool {
    chr == 'b'
        || chr == 'c'
        || chr == 'd'
        || chr == 'e'
        || chr == 'f'
}

fn get_open_directions(code: &str) -> Vec<Dir> {
    let hash = hasher::hash(code);
    let directions = hash.chars()
        .take(4)
        .enumerate()
        .filter_map(|(idx, chr)|
            if is_open(chr) {
                Some(Dir::from_val(idx))
            } else {None})
        .collect::<Vec<_>>();
    //let first = hash.;
    directions
}

#[derive(Debug,Clone)]
struct State {
    pos: (i32, i32),
    state: String
}

impl State {
    fn new(code: &str) -> State {
        State{pos: (0,3), state: code.to_owned() }
    }

    fn is_valid(&self) -> bool {
        self.pos.0 >= 0 && self.pos.0 < 4 && self.pos.1 >= 0 && self.pos.1 < 4
    }

    fn is_target(&self) -> bool {
        self.pos == (3,0)
    }

    fn move_dir(&self, direction: &Dir) -> Option<State> {
        let next = match *direction {
            Dir::Up => State{pos: (self.pos.0, self.pos.1 + 1),
                state: self.state.to_owned() + "U"
            },
            Dir::Down => State{pos: (self.pos.0, self.pos.1 - 1),
                state: self.state.to_owned() + "D"
            },
            Dir::Left => State{pos: (self.pos.0 - 1, self.pos.1),
                state: self.state.to_owned() + "L"
            },
            Dir::Right => State{pos: (self.pos.0 + 1, self.pos.1),
                state: self.state.to_owned() + "R"
            },
        };

        if next.is_valid() { Some(next) } else {None}
    }
}

// Recursively search and find the target position
fn find_path(start: State) -> Option<State> {

    if start.is_target() {
        return Some(start);
    }

    //println!("Find path on {}", start.state);
    get_open_directions(&start.state)
        .iter()
        .filter_map(|dir| start.move_dir(dir))
        .filter_map(|next| find_path(next))
        .min_by_key(|state| state.state.len())
}

// Recursively search and find the target position
fn find_longest_path(start: State) -> Option<State> {

    if start.is_target() {
        return Some(start);
    }

    //println!("Find path on {}", start.state);
    get_open_directions(&start.state)
        .iter()
        .filter_map(|dir| start.move_dir(dir))
        .filter_map(|next| find_longest_path(next))
        .max_by_key(|state| state.state.len())
}

fn main() {
    {
        let example_start = State::new("hijkl");
        let min_path = find_path(example_start);
        //println!("{:?}", min_path);
        assert!(min_path.is_none());
    }

    {
        let example_start = State::new("ihgpwlah");
        let min_path = find_path(example_start);
        //println!("{:?}", min_path);
        assert_eq!(min_path.unwrap().state, "ihgpwlahDDRRRD");
    }

    {
        let example_start = State::new("kglvqrro");
        let min_path = find_path(example_start);
        //println!("{:?}", min_path);
        assert_eq!(min_path.unwrap().state, "kglvqrroDDUDRLRRUDRD");
    }

    {
        let example_start = State::new("ulqzkmiv");
        let min_path = find_path(example_start);
        //println!("{:?}", min_path);
        assert_eq!(min_path.unwrap().state, "ulqzkmivDRURDRUDDLLDLUURRDULRLDUUDDDRR");
    }

    // Part 1
    let puzzle_input = "vwbaicqe";
    {
        let example_start = State::new(puzzle_input);
        let min_path = find_path(example_start);
        if let Some(min_path) = min_path {
            println!("Part 1: {}", &min_path.state[8..]);
            assert_eq!(min_path.state, "vwbaicqeDRDRULRDRD");
        }
    }

    // Part 2

    {
        let example_start = State::new("ihgpwlah");
        let max_path = find_longest_path(example_start);
        assert!(max_path.is_some());
        if let Some(max_path) = max_path {
            let length = max_path.state.len() - puzzle_input.len();
            //println!("max path length: {}", length);
            assert_eq!(length, 370);
        }
    }

    {
        let example_start = State::new("ulqzkmiv");
        let max_path = find_longest_path(example_start);
        assert!(max_path.is_some());
        if let Some(max_path) = max_path {
            let length = max_path.state.len() - puzzle_input.len();
            //println!("max path length: {}", length);
            assert_eq!(length, 830);
        }
    }

    {
        let example_start = State::new(puzzle_input);
        let max_path = find_longest_path(example_start);
        if let Some(max_path) = max_path {
            let length = max_path.state.len() - puzzle_input.len();
            println!("Part 2: max path length: {}", length);
            assert_eq!(length, 384);
        }
    }
}
<|endoftext|>


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

use super::{Binds, Statement, StatementMetadata};
use crate::mysql::{Mysql, MysqlType, MysqlValue};
use crate::result::QueryResult;
use crate::row::*;

pub struct StatementIterator<'a> {
    stmt: &'a mut Statement,
    output_binds: Binds,
    metadata: StatementMetadata,
}

#[allow(clippy::should_implement_trait)] // don't neet `Iterator` here
impl<'a> StatementIterator<'a> {
    #[allow(clippy::new_ret_no_self)]
    pub fn new(stmt: &'a mut Statement, types: Vec<Option<MysqlType>>) -> QueryResult<Self> {
        let metadata = stmt.metadata()?;
        let mut output_binds = if types.iter().any(Option::is_none) {
            Binds::from_output_types(types, Some(&metadata))
        } else {
            Binds::from_output_types(types, None)
        };

        stmt.execute_statement(&mut output_binds)?;

        Ok(StatementIterator {
            stmt,
            output_binds,
            metadata,
        })
    }

    pub fn map<F, T>(mut self, mut f: F) -> QueryResult<Vec<T>>
    where
        F: FnMut(MysqlRow) -> QueryResult<T>,
    {
        let mut results = Vec::new();
        while let Some(row) = self.next() {
            results.push(f(row?)?);
        }
        Ok(results)
    }

    fn next(&mut self) -> Option<QueryResult<MysqlRow>> {
        match self.stmt.populate_row_buffers(&mut self.output_binds) {
            Ok(Some(())) => Some(Ok(MysqlRow {
                col_idx: 0,
                binds: &mut self.output_binds,
                metadata: &self.metadata,
            })),
            Ok(None) => None,
            Err(e) => Some(Err(e)),
        }
    }
}

pub struct MysqlRow<'a> {
    col_idx: usize,
    binds: &'a Binds,
    metadata: &'a StatementMetadata,
}

impl<'a> Row<Mysql> for MysqlRow<'a> {
    fn take(&mut self) -> Option<MysqlValue<'_>> {
        let current_idx = self.col_idx;
        self.col_idx += 1;
        self.binds.field_data(current_idx)
    }

    fn next_is_null(&self, count: usize) -> bool {
        (0..count).all(|i| self.binds.field_data(self.col_idx + i).is_none())
    }

    fn column_count(&self) -> usize {
        self.binds.len()
    }

    fn column_name(&self) -> Option<&str> {
        self.metadata.fields()[self.col_idx].field_name()
    }
}

pub struct NamedStatementIterator<'a> {
    stmt: &'a mut Statement,
    output_binds: Binds,
    metadata: StatementMetadata,
}

#[allow(clippy::should_implement_trait)] // don't need `Iterator` here
impl<'a> NamedStatementIterator<'a> {
    #[allow(clippy::new_ret_no_self)]
    pub fn new(stmt: &'a mut Statement) -> QueryResult<Self> {
        let metadata = stmt.metadata()?;
        let mut output_binds = Binds::from_result_metadata(&metadata);

        stmt.execute_statement(&mut output_binds)?;

        Ok(NamedStatementIterator {
            stmt,
            output_binds,
            metadata,
        })
    }

    pub fn map<F, T>(mut self, mut f: F) -> QueryResult<Vec<T>>
    where
        F: FnMut(NamedMysqlRow) -> QueryResult<T>,
    {
        let mut results = Vec::new();
        while let Some(row) = self.next() {
            results.push(f(row?)?);
        }
        Ok(results)
    }

    fn next(&mut self) -> Option<QueryResult<NamedMysqlRow>> {
        match self.stmt.populate_row_buffers(&mut self.output_binds) {
            Ok(Some(())) => Some(Ok(NamedMysqlRow {
                binds: &self.output_binds,
                column_indices: self.metadata.column_indices(),
            })),
            Ok(None) => None,
            Err(e) => Some(Err(e)),
        }
    }
}

pub struct NamedMysqlRow<'a> {
    binds: &'a Binds,
    column_indices: &'a HashMap<&'a str, usize>,
}

impl<'a> NamedRow<Mysql> for NamedMysqlRow<'a> {
    fn index_of(&self, column_name: &str) -> Option<usize> {
        self.column_indices.get(column_name).cloned()
    }

    fn get_raw_value(&self, idx: usize) -> Option<MysqlValue<'_>> {
        self.binds.field_data(idx)
    }

    fn field_names(&self) -> Vec<&str> {
        todo!()
    }
}
<commit_msg>Implement `NamedRow` changes for mysql<commit_after>use std::collections::HashMap;

use super::{Binds, Statement, StatementMetadata};
use crate::mysql::{Mysql, MysqlType, MysqlValue};
use crate::result::QueryResult;
use crate::row::*;

pub struct StatementIterator<'a> {
    stmt: &'a mut Statement,
    output_binds: Binds,
    metadata: StatementMetadata,
}

#[allow(clippy::should_implement_trait)] // don't neet `Iterator` here
impl<'a> StatementIterator<'a> {
    #[allow(clippy::new_ret_no_self)]
    pub fn new(stmt: &'a mut Statement, types: Vec<Option<MysqlType>>) -> QueryResult<Self> {
        let metadata = stmt.metadata()?;
        let mut output_binds = if types.iter().any(Option::is_none) {
            Binds::from_output_types(types, Some(&metadata))
        } else {
            Binds::from_output_types(types, None)
        };

        stmt.execute_statement(&mut output_binds)?;

        Ok(StatementIterator {
            stmt,
            output_binds,
            metadata,
        })
    }

    pub fn map<F, T>(mut self, mut f: F) -> QueryResult<Vec<T>>
    where
        F: FnMut(MysqlRow) -> QueryResult<T>,
    {
        let mut results = Vec::new();
        while let Some(row) = self.next() {
            results.push(f(row?)?);
        }
        Ok(results)
    }

    fn next(&mut self) -> Option<QueryResult<MysqlRow>> {
        match self.stmt.populate_row_buffers(&mut self.output_binds) {
            Ok(Some(())) => Some(Ok(MysqlRow {
                col_idx: 0,
                binds: &mut self.output_binds,
                metadata: &self.metadata,
            })),
            Ok(None) => None,
            Err(e) => Some(Err(e)),
        }
    }
}

pub struct MysqlRow<'a> {
    col_idx: usize,
    binds: &'a Binds,
    metadata: &'a StatementMetadata,
}

impl<'a> Row<Mysql> for MysqlRow<'a> {
    fn take(&mut self) -> Option<MysqlValue<'_>> {
        let current_idx = self.col_idx;
        self.col_idx += 1;
        self.binds.field_data(current_idx)
    }

    fn next_is_null(&self, count: usize) -> bool {
        (0..count).all(|i| self.binds.field_data(self.col_idx + i).is_none())
    }

    fn column_count(&self) -> usize {
        self.binds.len()
    }

    fn column_name(&self) -> Option<&str> {
        self.metadata.fields()[self.col_idx].field_name()
    }
}

pub struct NamedStatementIterator<'a> {
    stmt: &'a mut Statement,
    output_binds: Binds,
    metadata: StatementMetadata,
}

#[allow(clippy::should_implement_trait)] // don't need `Iterator` here
impl<'a> NamedStatementIterator<'a> {
    #[allow(clippy::new_ret_no_self)]
    pub fn new(stmt: &'a mut Statement) -> QueryResult<Self> {
        let metadata = stmt.metadata()?;
        let mut output_binds = Binds::from_result_metadata(&metadata);

        stmt.execute_statement(&mut output_binds)?;

        Ok(NamedStatementIterator {
            stmt,
            output_binds,
            metadata,
        })
    }

    pub fn map<F, T>(mut self, mut f: F) -> QueryResult<Vec<T>>
    where
        F: FnMut(NamedMysqlRow) -> QueryResult<T>,
    {
        let mut results = Vec::new();
        while let Some(row) = self.next() {
            results.push(f(row?)?);
        }
        Ok(results)
    }

    fn next(&mut self) -> Option<QueryResult<NamedMysqlRow>> {
        match self.stmt.populate_row_buffers(&mut self.output_binds) {
            Ok(Some(())) => Some(Ok(NamedMysqlRow {
                binds: &self.output_binds,
                column_indices: self.metadata.column_indices(),
                metadata: &self.metadata,
            })),
            Ok(None) => None,
            Err(e) => Some(Err(e)),
        }
    }
}

pub struct NamedMysqlRow<'a> {
    binds: &'a Binds,
    column_indices: &'a HashMap<&'a str, usize>,
    metadata: &'a StatementMetadata,
}

impl<'a> NamedRow<Mysql> for NamedMysqlRow<'a> {
    fn index_of(&self, column_name: &str) -> Option<usize> {
        self.column_indices.get(column_name).cloned()
    }

    fn get_raw_value(&self, idx: usize) -> Option<MysqlValue<'_>> {
        self.binds.field_data(idx)
    }

    fn field_names(&self) -> Vec<&str> {
        self.metadata
            .fields()
            .iter()
            .filter_map(|f| f.field_name())
            .collect()
    }
}

<|endoftext|>


<commit_before>use crate::common::util::*;

#[test]
fn test_to_upper() {
    new_ucmd!()
        .args(&["a-z", "A-Z"])
        .pipe_in("!abcd!")
        .run()
        .stdout_is("!ABCD!");
}

#[test]
fn test_small_set2() {
    new_ucmd!()
        .args(&["0-9", "X"])
        .pipe_in("@0123456789")
        .run()
        .stdout_is("@XXXXXXXXXX");
}

#[test]
fn test_unicode() {
    new_ucmd!()
        .args(&[", ┬─┬", "╯︵┻━┻"])
        .pipe_in("(,°□°）, ┬─┬")
        .run()
        .stdout_is("(╯°□°）╯︵┻━┻");
}

#[test]
fn test_delete() {
    new_ucmd!()
        .args(&["-d", "a-z"])
        .pipe_in("aBcD")
        .run()
        .stdout_is("BD");
}

#[test]
fn test_delete_complement() {
    new_ucmd!()
        .args(&["-d", "-c", "a-z"])
        .pipe_in("aBcD")
        .run()
        .stdout_is("ac");
}

#[test]
fn test_delete_complement_2() {
    new_ucmd!()
        .args(&["-d", "-C", "0-9"])
        .pipe_in("Phone: 01234 567890")
        .succeeds()
        .stdout_is("01234567890");
    new_ucmd!()
        .args(&["-d", "--complement", "0-9"])
        .pipe_in("Phone: 01234 567890")
        .succeeds()
        .stdout_is("01234567890");
}

#[test]
fn test_complement1() {
    new_ucmd!()
        .args(&["-c", "a", "X"])
        .pipe_in("ab")
        .run()
        .stdout_is("aX");
}

#[test]
fn test_complement2() {
    new_ucmd!()
        .args(&["-c", "0-9", "x"])
        .pipe_in("Phone: 01234 567890")
        .run()
        .stdout_is("xxxxxxx01234x567890");
}

#[test]
fn test_complement3() {
    new_ucmd!()
        .args(&["-c", "abcdefgh", "123"]) // spell-checker:disable-line
        .pipe_in("the cat and the bat")
        .run()
        .stdout_is("3he3ca33a3d33he3ba3"); // spell-checker:disable-line
}

#[test]
fn test_complement4() {
    // $ echo -n '0x1y2z3' | tr -c '0-@' '*-~'
    // 0~1~2~3
    new_ucmd!()
        .args(&["-c", "0-@", "*-~"])
        .pipe_in("0x1y2z3")
        .run()
        .stdout_is("0~1~2~3");
}

#[test]
#[ignore = "fixme: GNU tr returns '0a1b2c3' instead of '0~1~2~3', see #2158"]
fn test_complement5() {
    // $ echo '0x1y2z3' | tr -c '\0-@' '*-~'
    // 0a1b2c3
    new_ucmd!()
        .args(&["-c", "\\0-@", "*-~"])
        .pipe_in("0x1y2z3")
        .run()
        .stdout_is("0a1b2c3");
}

#[test]
fn test_squeeze() {
    new_ucmd!()
        .args(&["-s", "a-z"])
        .pipe_in("aaBBcDcc")
        .run()
        .stdout_is("aBBcDc");
}

#[test]
fn test_squeeze_complement() {
    new_ucmd!()
        .args(&["-sc", "a-z"])
        .pipe_in("aaBBcDcc")
        .run()
        .stdout_is("aaBcDcc");
}

#[test]
fn test_squeeze_complement_two_sets() {
    new_ucmd!()
        .args(&["-sc", "a", "_"])
        .pipe_in("test a aa with 3 ___ spaaaces +++") // spell-checker:disable-line
        .run()
        .stdout_is("_a_aa_aaa_");
}

#[test]
fn test_translate_and_squeeze() {
    new_ucmd!()
        .args(&["-s", "x", "y"])
        .pipe_in("xx")
        .run()
        .stdout_is("y");
}

#[test]
fn test_translate_and_squeeze_multiple_lines() {
    new_ucmd!()
        .args(&["-s", "x", "y"])
        .pipe_in("xxaax\nxaaxx") // spell-checker:disable-line
        .run()
        .stdout_is("yaay\nyaay"); // spell-checker:disable-line
}

#[test]
fn test_delete_and_squeeze() {
    new_ucmd!()
        .args(&["-ds", "a-z", "A-Z"])
        .pipe_in("abBcB")
        .run()
        .stdout_is("B");
}

#[test]
fn test_delete_and_squeeze_complement() {
    new_ucmd!()
        .args(&["-dsc", "a-z", "A-Z"])
        .pipe_in("abBcB")
        .run()
        .stdout_is("abc");
}

#[test]
fn test_set1_longer_than_set2() {
    new_ucmd!()
        .args(&["abc", "xy"])
        .pipe_in("abcde")
        .run()
        .stdout_is("xyyde"); // spell-checker:disable-line
}

#[test]
fn test_set1_shorter_than_set2() {
    new_ucmd!()
        .args(&["ab", "xyz"])
        .pipe_in("abcde")
        .run()
        .stdout_is("xycde"); // spell-checker:disable-line
}

#[test]
fn test_truncate() {
    new_ucmd!()
        .args(&["-t", "abc", "xy"])
        .pipe_in("abcde")
        .run()
        .stdout_is("xycde"); // spell-checker:disable-line
}

#[test]
fn test_truncate_with_set1_shorter_than_set2() {
    new_ucmd!()
        .args(&["-t", "ab", "xyz"])
        .pipe_in("abcde")
        .run()
        .stdout_is("xycde"); // spell-checker:disable-line
}

#[test]
fn missing_args_fails() {
    let (_, mut ucmd) = at_and_ucmd!();
    ucmd.fails().stderr_contains("missing operand");
}

#[test]
fn missing_required_second_arg_fails() {
    let (_, mut ucmd) = at_and_ucmd!();
    ucmd.args(&["foo"])
        .fails()
        .stderr_contains("missing operand after");
}

#[test]
fn test_interpret_backslash_escapes() {
    new_ucmd!()
        .args(&["abfnrtv", r"\a\b\f\n\r\t\v"]) // spell-checker:disable-line
        .pipe_in("abfnrtv") // spell-checker:disable-line
        .succeeds()
        .stdout_is("\u{7}\u{8}\u{c}\n\r\t\u{b}");
}

#[test]
fn test_interpret_unrecognized_backslash_escape_as_character() {
    new_ucmd!()
        .args(&["qcz+=~-", r"\q\c\z\+\=\~\-"])
        .pipe_in("qcz+=~-")
        .succeeds()
        .stdout_is("qcz+=~-");
}

#[test]
fn test_interpret_single_octal_escape() {
    new_ucmd!()
        .args(&["X", r"\015"])
        .pipe_in("X")
        .succeeds()
        .stdout_is("\r");
}

#[test]
fn test_interpret_one_and_two_digit_octal_escape() {
    new_ucmd!()
        .args(&["XYZ", r"\0\11\77"])
        .pipe_in("XYZ")
        .succeeds()
        .stdout_is("\0\t?");
}

#[test]
fn test_octal_escape_is_at_most_three_digits() {
    new_ucmd!()
        .args(&["XY", r"\0156"])
        .pipe_in("XY")
        .succeeds()
        .stdout_is("\r6");
}

#[test]
fn test_non_octal_digit_ends_escape() {
    new_ucmd!()
        .args(&["rust", r"\08\11956"])
        .pipe_in("rust")
        .succeeds()
        .stdout_is("\08\t9");
}

#[test]
fn test_interpret_backslash_at_eol_literally() {
    new_ucmd!()
        .args(&["X", r"\"])
        .pipe_in("X")
        .succeeds()
        .stdout_is("\\");
}

#[test]
fn test_more_than_2_sets() {
    new_ucmd!()
        .args(&["'abcdefgh'", "'a", "'b'"])
        .pipe_in("hello world")
        .fails();
}
<commit_msg>Silent 'test_more_than_2_sets' on freebsd<commit_after>use crate::common::util::*;

#[test]
fn test_to_upper() {
    new_ucmd!()
        .args(&["a-z", "A-Z"])
        .pipe_in("!abcd!")
        .run()
        .stdout_is("!ABCD!");
}

#[test]
fn test_small_set2() {
    new_ucmd!()
        .args(&["0-9", "X"])
        .pipe_in("@0123456789")
        .run()
        .stdout_is("@XXXXXXXXXX");
}

#[test]
fn test_unicode() {
    new_ucmd!()
        .args(&[", ┬─┬", "╯︵┻━┻"])
        .pipe_in("(,°□°）, ┬─┬")
        .run()
        .stdout_is("(╯°□°）╯︵┻━┻");
}

#[test]
fn test_delete() {
    new_ucmd!()
        .args(&["-d", "a-z"])
        .pipe_in("aBcD")
        .run()
        .stdout_is("BD");
}

#[test]
fn test_delete_complement() {
    new_ucmd!()
        .args(&["-d", "-c", "a-z"])
        .pipe_in("aBcD")
        .run()
        .stdout_is("ac");
}

#[test]
fn test_delete_complement_2() {
    new_ucmd!()
        .args(&["-d", "-C", "0-9"])
        .pipe_in("Phone: 01234 567890")
        .succeeds()
        .stdout_is("01234567890");
    new_ucmd!()
        .args(&["-d", "--complement", "0-9"])
        .pipe_in("Phone: 01234 567890")
        .succeeds()
        .stdout_is("01234567890");
}

#[test]
fn test_complement1() {
    new_ucmd!()
        .args(&["-c", "a", "X"])
        .pipe_in("ab")
        .run()
        .stdout_is("aX");
}

#[test]
fn test_complement2() {
    new_ucmd!()
        .args(&["-c", "0-9", "x"])
        .pipe_in("Phone: 01234 567890")
        .run()
        .stdout_is("xxxxxxx01234x567890");
}

#[test]
fn test_complement3() {
    new_ucmd!()
        .args(&["-c", "abcdefgh", "123"]) // spell-checker:disable-line
        .pipe_in("the cat and the bat")
        .run()
        .stdout_is("3he3ca33a3d33he3ba3"); // spell-checker:disable-line
}

#[test]
fn test_complement4() {
    // $ echo -n '0x1y2z3' | tr -c '0-@' '*-~'
    // 0~1~2~3
    new_ucmd!()
        .args(&["-c", "0-@", "*-~"])
        .pipe_in("0x1y2z3")
        .run()
        .stdout_is("0~1~2~3");
}

#[test]
#[ignore = "fixme: GNU tr returns '0a1b2c3' instead of '0~1~2~3', see #2158"]
fn test_complement5() {
    // $ echo '0x1y2z3' | tr -c '\0-@' '*-~'
    // 0a1b2c3
    new_ucmd!()
        .args(&["-c", "\\0-@", "*-~"])
        .pipe_in("0x1y2z3")
        .run()
        .stdout_is("0a1b2c3");
}

#[test]
fn test_squeeze() {
    new_ucmd!()
        .args(&["-s", "a-z"])
        .pipe_in("aaBBcDcc")
        .run()
        .stdout_is("aBBcDc");
}

#[test]
fn test_squeeze_complement() {
    new_ucmd!()
        .args(&["-sc", "a-z"])
        .pipe_in("aaBBcDcc")
        .run()
        .stdout_is("aaBcDcc");
}

#[test]
fn test_squeeze_complement_two_sets() {
    new_ucmd!()
        .args(&["-sc", "a", "_"])
        .pipe_in("test a aa with 3 ___ spaaaces +++") // spell-checker:disable-line
        .run()
        .stdout_is("_a_aa_aaa_");
}

#[test]
fn test_translate_and_squeeze() {
    new_ucmd!()
        .args(&["-s", "x", "y"])
        .pipe_in("xx")
        .run()
        .stdout_is("y");
}

#[test]
fn test_translate_and_squeeze_multiple_lines() {
    new_ucmd!()
        .args(&["-s", "x", "y"])
        .pipe_in("xxaax\nxaaxx") // spell-checker:disable-line
        .run()
        .stdout_is("yaay\nyaay"); // spell-checker:disable-line
}

#[test]
fn test_delete_and_squeeze() {
    new_ucmd!()
        .args(&["-ds", "a-z", "A-Z"])
        .pipe_in("abBcB")
        .run()
        .stdout_is("B");
}

#[test]
fn test_delete_and_squeeze_complement() {
    new_ucmd!()
        .args(&["-dsc", "a-z", "A-Z"])
        .pipe_in("abBcB")
        .run()
        .stdout_is("abc");
}

#[test]
fn test_set1_longer_than_set2() {
    new_ucmd!()
        .args(&["abc", "xy"])
        .pipe_in("abcde")
        .run()
        .stdout_is("xyyde"); // spell-checker:disable-line
}

#[test]
fn test_set1_shorter_than_set2() {
    new_ucmd!()
        .args(&["ab", "xyz"])
        .pipe_in("abcde")
        .run()
        .stdout_is("xycde"); // spell-checker:disable-line
}

#[test]
fn test_truncate() {
    new_ucmd!()
        .args(&["-t", "abc", "xy"])
        .pipe_in("abcde")
        .run()
        .stdout_is("xycde"); // spell-checker:disable-line
}

#[test]
fn test_truncate_with_set1_shorter_than_set2() {
    new_ucmd!()
        .args(&["-t", "ab", "xyz"])
        .pipe_in("abcde")
        .run()
        .stdout_is("xycde"); // spell-checker:disable-line
}

#[test]
fn missing_args_fails() {
    let (_, mut ucmd) = at_and_ucmd!();
    ucmd.fails().stderr_contains("missing operand");
}

#[test]
fn missing_required_second_arg_fails() {
    let (_, mut ucmd) = at_and_ucmd!();
    ucmd.args(&["foo"])
        .fails()
        .stderr_contains("missing operand after");
}

#[test]
fn test_interpret_backslash_escapes() {
    new_ucmd!()
        .args(&["abfnrtv", r"\a\b\f\n\r\t\v"]) // spell-checker:disable-line
        .pipe_in("abfnrtv") // spell-checker:disable-line
        .succeeds()
        .stdout_is("\u{7}\u{8}\u{c}\n\r\t\u{b}");
}

#[test]
fn test_interpret_unrecognized_backslash_escape_as_character() {
    new_ucmd!()
        .args(&["qcz+=~-", r"\q\c\z\+\=\~\-"])
        .pipe_in("qcz+=~-")
        .succeeds()
        .stdout_is("qcz+=~-");
}

#[test]
fn test_interpret_single_octal_escape() {
    new_ucmd!()
        .args(&["X", r"\015"])
        .pipe_in("X")
        .succeeds()
        .stdout_is("\r");
}

#[test]
fn test_interpret_one_and_two_digit_octal_escape() {
    new_ucmd!()
        .args(&["XYZ", r"\0\11\77"])
        .pipe_in("XYZ")
        .succeeds()
        .stdout_is("\0\t?");
}

#[test]
fn test_octal_escape_is_at_most_three_digits() {
    new_ucmd!()
        .args(&["XY", r"\0156"])
        .pipe_in("XY")
        .succeeds()
        .stdout_is("\r6");
}

#[test]
fn test_non_octal_digit_ends_escape() {
    new_ucmd!()
        .args(&["rust", r"\08\11956"])
        .pipe_in("rust")
        .succeeds()
        .stdout_is("\08\t9");
}

#[test]
fn test_interpret_backslash_at_eol_literally() {
    new_ucmd!()
        .args(&["X", r"\"])
        .pipe_in("X")
        .succeeds()
        .stdout_is("\\");
}

#[test]
// FixME: panicked at 'failed to write to stdin of child: Broken pipe (os error 32)
#[cfg(not(target_os = "freebsd"))]
fn test_more_than_2_sets() {
    new_ucmd!()
        .args(&["'abcdefgh'", "'a", "'b'"])
        .pipe_in("hello world")
        .fails();
}
<|endoftext|>