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

use std::collections::HashMap;
use std::fmt;
use std::hash::Hash;
use std::slice::Iter;

use common::ArgumentName;
use common::DirectiveName;
use common::Named;
use common::NamedItem;
use common::WithLocation;
use graphql_syntax::ConstantValue;
use graphql_syntax::DirectiveLocation;
use intern::string_key::Intern;
use intern::string_key::StringKey;
use lazy_static::lazy_static;

lazy_static! {
    static ref DIRECTIVE_DEPRECATED: DirectiveName = DirectiveName("deprecated".intern());
    static ref ARGUMENT_REASON: ArgumentName = ArgumentName("reason".intern());
}

pub(crate) type TypeMap = HashMap<StringKey, Type>;

macro_rules! type_id {
    ($name:ident, $type:ident) => {
        #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
        pub struct $name(pub $type);
        impl $name {
            pub(crate) fn as_usize(&self) -> usize {
                self.0 as usize
            }
        }

        impl fmt::Debug for $name {
            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                write!(f, "{}({})", stringify!($name), self.0)
            }
        }

        impl fmt::Display for $name {
            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                write!(f, "{}", self.0)
            }
        }
    };
}

type_id!(EnumID, u32);
type_id!(InputObjectID, u32);
type_id!(InterfaceID, u32);
type_id!(ObjectID, u32);
type_id!(ScalarID, u32);
type_id!(UnionID, u32);
type_id!(FieldID, u32);

#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum Type {
    Enum(EnumID),
    InputObject(InputObjectID),
    Interface(InterfaceID),
    Object(ObjectID),
    Scalar(ScalarID),
    Union(UnionID),
}

impl Type {
    pub fn is_scalar(self) -> bool {
        matches!(self, Type::Scalar(_))
    }

    pub fn is_enum(self) -> bool {
        matches!(self, Type::Enum(_))
    }

    pub fn is_input_type(self) -> bool {
        matches!(self, Type::Scalar(_) | Type::Enum(_) | Type::InputObject(_))
    }

    pub fn is_abstract_type(self) -> bool {
        matches!(self, Type::Union(_) | Type::Interface(_))
    }

    pub fn is_composite_type(self) -> bool {
        matches!(self, Type::Object(_) | Type::Interface(_) | Type::Union(_))
    }

    pub fn is_object(self) -> bool {
        matches!(self, Type::Object(_))
    }

    pub fn is_input_object(self) -> bool {
        matches!(self, Type::InputObject(_))
    }

    pub fn is_interface(self) -> bool {
        matches!(self, Type::Interface(_))
    }

    pub fn is_object_or_interface(self) -> bool {
        matches!(self, Type::Object(_) | Type::Interface(_))
    }

    pub fn is_union(self) -> bool {
        matches!(self, Type::Union(_))
    }

    pub fn get_enum_id(self) -> Option<EnumID> {
        match self {
            Type::Enum(id) => Some(id),
            _ => None,
        }
    }

    pub fn get_input_object_id(self) -> Option<InputObjectID> {
        match self {
            Type::InputObject(id) => Some(id),
            _ => None,
        }
    }

    pub fn get_interface_id(self) -> Option<InterfaceID> {
        match self {
            Type::Interface(id) => Some(id),
            _ => None,
        }
    }

    pub fn get_object_id(self) -> Option<ObjectID> {
        match self {
            Type::Object(id) => Some(id),
            _ => None,
        }
    }

    pub fn get_scalar_id(self) -> Option<ScalarID> {
        match self {
            Type::Scalar(id) => Some(id),
            _ => None,
        }
    }

    pub fn get_union_id(self) -> Option<UnionID> {
        match self {
            Type::Union(id) => Some(id),
            _ => None,
        }
    }
}

impl fmt::Debug for Type {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Type::Enum(id) => f.write_fmt(format_args!("Enum({:?})", id.0)),
            Type::InputObject(id) => f.write_fmt(format_args!("InputObject({:?})", id.0)),
            Type::Interface(id) => f.write_fmt(format_args!("Interface({:?})", id.0)),
            Type::Object(id) => f.write_fmt(format_args!("Object({:?})", id.0)),
            Type::Scalar(id) => f.write_fmt(format_args!("Scalar({:?})", id.0)),
            Type::Union(id) => f.write_fmt(format_args!("Union({:?})", id.0)),
        }
    }
}

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum TypeReference {
    Named(Type),
    NonNull(Box<TypeReference>),
    List(Box<TypeReference>),
}

impl TypeReference {
    pub fn inner(&self) -> Type {
        match self {
            TypeReference::Named(type_) => *type_,
            TypeReference::List(of) => of.inner(),
            TypeReference::NonNull(of) => of.inner(),
        }
    }

    pub fn non_null(&self) -> TypeReference {
        match self {
            TypeReference::Named(_) => TypeReference::NonNull(Box::new(self.clone())),
            TypeReference::List(_) => TypeReference::NonNull(Box::new(self.clone())),
            TypeReference::NonNull(_) => self.clone(),
        }
    }

    pub fn nullable_type(&self) -> &TypeReference {
        match self {
            TypeReference::Named(_) => self,
            TypeReference::List(_) => self,
            TypeReference::NonNull(of) => of,
        }
    }

    pub fn is_non_null(&self) -> bool {
        matches!(self, TypeReference::NonNull(_))
    }

    pub fn is_list(&self) -> bool {
        matches!(self.nullable_type(), TypeReference::List(_))
    }

    // If the type is Named or NonNull<Named> return the inner named.
    // If the type is a List or NonNull<List> returns a matching list with nullable items.
    pub fn with_nullable_item_type(&self) -> TypeReference {
        match self {
            TypeReference::Named(_) => self.clone(),
            TypeReference::List(of) => TypeReference::List(Box::new(of.nullable_type().clone())),
            TypeReference::NonNull(of) => {
                let inner: &TypeReference = of;
                match inner {
                    TypeReference::List(_) => {
                        TypeReference::NonNull(Box::new(of.with_nullable_item_type()))
                    }
                    TypeReference::Named(_) => inner.clone(),
                    TypeReference::NonNull(_) => {
                        unreachable!("Invalid nested TypeReference::NonNull")
                    }
                }
            }
        }
    }

    pub fn list_item_type(&self) -> Option<&TypeReference> {
        match self.nullable_type() {
            TypeReference::List(of) => Some(of),
            _ => None,
        }
    }

    // Return None if the type is a List, otherwise return the inner type
    pub fn non_list_type(&self) -> Option<Type> {
        match self {
            TypeReference::List(_) => None,
            TypeReference::Named(type_) => Some(*type_),
            TypeReference::NonNull(of) => of.non_list_type(),
        }
    }
}

#[derive(Clone, Debug)]
pub struct Directive {
    pub name: DirectiveName,
    pub arguments: ArgumentDefinitions,
    pub locations: Vec<DirectiveLocation>,
    pub repeatable: bool,
    pub is_extension: bool,
    pub description: Option<StringKey>,
}

impl Named for Directive {
    type Name = DirectiveName;
    fn name(&self) -> DirectiveName {
        self.name
    }
}

#[derive(Clone, Debug)]
pub struct Scalar {
    pub name: WithLocation<StringKey>,
    pub is_extension: bool,
    pub directives: Vec<DirectiveValue>,
    pub description: Option<StringKey>,
}

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct Object {
    pub name: WithLocation<StringKey>,
    pub is_extension: bool,
    pub fields: Vec<FieldID>,
    pub interfaces: Vec<InterfaceID>,
    pub directives: Vec<DirectiveValue>,
    pub description: Option<StringKey>,
}

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct InputObject {
    pub name: WithLocation<StringKey>,
    pub fields: ArgumentDefinitions,
    pub directives: Vec<DirectiveValue>,
    pub description: Option<StringKey>,
}

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct Enum {
    pub name: WithLocation<StringKey>,
    pub is_extension: bool,
    pub values: Vec<EnumValue>,
    pub directives: Vec<DirectiveValue>,
    pub description: Option<StringKey>,
}

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct Union {
    pub name: WithLocation<StringKey>,
    pub is_extension: bool,
    pub members: Vec<ObjectID>,
    pub directives: Vec<DirectiveValue>,
    pub description: Option<StringKey>,
}

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct Interface {
    pub name: WithLocation<StringKey>,
    pub is_extension: bool,
    pub implementing_interfaces: Vec<InterfaceID>,
    pub implementing_objects: Vec<ObjectID>,
    pub fields: Vec<FieldID>,
    pub directives: Vec<DirectiveValue>,
    pub interfaces: Vec<InterfaceID>,
    pub description: Option<StringKey>,
}

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct Field {
    pub name: WithLocation<StringKey>,
    pub is_extension: bool,
    pub arguments: ArgumentDefinitions,
    pub type_: TypeReference,
    pub directives: Vec<DirectiveValue>,
    /// The type on which this field was defined. This field is (should)
    /// always be set, except for special fields such as __typename and
    /// __id, which are queryable on all types and therefore don't have
    /// a single parent type.
    pub parent_type: Option<Type>,
    pub description: Option<StringKey>,
}

pub struct Deprecation {
    pub reason: Option<StringKey>,
}

impl Field {
    pub fn deprecated(&self) -> Option<Deprecation> {
        self.directives
            .named(*DIRECTIVE_DEPRECATED)
            .map(|directive| Deprecation {
                reason: directive
                    .arguments
                    .named(*ARGUMENT_REASON)
                    .and_then(|reason| reason.value.get_string_literal()),
            })
    }
}

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Argument {
    pub name: ArgumentName,
    pub type_: TypeReference,
    pub default_value: Option<ConstantValue>,
    pub description: Option<StringKey>,
    pub directives: Vec<DirectiveValue>,
}

impl Named for Argument {
    type Name = ArgumentName;
    fn name(&self) -> ArgumentName {
        self.name
    }
}

#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct ArgumentValue {
    pub name: ArgumentName,
    pub value: ConstantValue,
}

#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct DirectiveValue {
    pub name: DirectiveName,
    pub arguments: Vec<ArgumentValue>,
}

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct EnumValue {
    pub value: StringKey,
    pub directives: Vec<DirectiveValue>,
}

#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct ArgumentDefinitions(pub(crate) Vec<Argument>);

impl ArgumentDefinitions {
    pub fn new(arguments: Vec<Argument>) -> Self {
        Self(arguments)
    }

    pub fn named(&self, name: ArgumentName) -> Option<&Argument> {
        self.0.named(name)
    }

    pub fn contains(&self, name: StringKey) -> bool {
        self.0.iter().any(|x| x.name == ArgumentName(name))
    }

    pub fn iter(&self) -> Iter<'_, Argument> {
        self.0.iter()
    }

    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }
}

impl fmt::Debug for ArgumentDefinitions {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_fmt(format_args!("{:#?}", self.0))
    }
}

impl IntoIterator for ArgumentDefinitions {
    type Item = Argument;
    type IntoIter = std::vec::IntoIter<Self::Item>;

    fn into_iter(self) -> Self::IntoIter {
        self.0.into_iter()
    }
}

pub trait TypeWithFields {
    fn fields(&self) -> &Vec<FieldID>;
    fn interfaces(&self) -> &Vec<InterfaceID>;
}

impl TypeWithFields for Interface {
    fn fields(&self) -> &Vec<FieldID> {
        &self.fields
    }

    fn interfaces(&self) -> &Vec<InterfaceID> {
        &self.interfaces
    }
}

impl TypeWithFields for Object {
    fn fields(&self) -> &Vec<FieldID> {
        &self.fields
    }

    fn interfaces(&self) -> &Vec<InterfaceID> {
        &self.interfaces
    }
}

#[allow(unused_macros)]
macro_rules! impl_named {
    ($type_name:ident) => {
        impl Named for $type_name {
            type Name = StringKey;
            fn name(&self) -> StringKey {
                self.name
            }
        }
    };
}

macro_rules! impl_named_for_with_location {
    ($type_name:ident) => {
        impl Named for $type_name {
            type Name = StringKey;
            fn name(&self) -> StringKey {
                self.name.item
            }
        }
    };
}

impl_named_for_with_location!(Object);
impl_named_for_with_location!(Field);
impl_named_for_with_location!(InputObject);
impl_named_for_with_location!(Interface);
impl_named_for_with_location!(Union);
impl_named_for_with_location!(Scalar);
impl_named_for_with_location!(Enum);

impl Named for DirectiveValue {
    type Name = DirectiveName;
    fn name(&self) -> DirectiveName {
        self.name
    }
}

impl Named for ArgumentValue {
    type Name = ArgumentName;
    fn name(&self) -> ArgumentName {
        self.name
    }
}
<commit_msg>Parse other types of directive change<commit_after>/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use std::collections::HashMap;
use std::fmt;
use std::hash::Hash;
use std::slice::Iter;

use common::ArgumentName;
use common::DirectiveName;
use common::Named;
use common::NamedItem;
use common::WithLocation;
use graphql_syntax::ConstantValue;
use graphql_syntax::DirectiveLocation;
use intern::string_key::Intern;
use intern::string_key::StringKey;
use lazy_static::lazy_static;

lazy_static! {
    static ref DIRECTIVE_DEPRECATED: DirectiveName = DirectiveName("deprecated".intern());
    static ref ARGUMENT_REASON: ArgumentName = ArgumentName("reason".intern());
}

pub(crate) type TypeMap = HashMap<StringKey, Type>;

macro_rules! type_id {
    ($name:ident, $type:ident) => {
        #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
        pub struct $name(pub $type);
        impl $name {
            pub(crate) fn as_usize(&self) -> usize {
                self.0 as usize
            }
        }

        impl fmt::Debug for $name {
            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                write!(f, "{}({})", stringify!($name), self.0)
            }
        }

        impl fmt::Display for $name {
            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                write!(f, "{}", self.0)
            }
        }
    };
}

type_id!(EnumID, u32);
type_id!(InputObjectID, u32);
type_id!(InterfaceID, u32);
type_id!(ObjectID, u32);
type_id!(ScalarID, u32);
type_id!(UnionID, u32);
type_id!(FieldID, u32);

#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum Type {
    Enum(EnumID),
    InputObject(InputObjectID),
    Interface(InterfaceID),
    Object(ObjectID),
    Scalar(ScalarID),
    Union(UnionID),
}

impl Type {
    pub fn is_scalar(self) -> bool {
        matches!(self, Type::Scalar(_))
    }

    pub fn is_enum(self) -> bool {
        matches!(self, Type::Enum(_))
    }

    pub fn is_input_type(self) -> bool {
        matches!(self, Type::Scalar(_) | Type::Enum(_) | Type::InputObject(_))
    }

    pub fn is_abstract_type(self) -> bool {
        matches!(self, Type::Union(_) | Type::Interface(_))
    }

    pub fn is_composite_type(self) -> bool {
        matches!(self, Type::Object(_) | Type::Interface(_) | Type::Union(_))
    }

    pub fn is_object(self) -> bool {
        matches!(self, Type::Object(_))
    }

    pub fn is_input_object(self) -> bool {
        matches!(self, Type::InputObject(_))
    }

    pub fn is_interface(self) -> bool {
        matches!(self, Type::Interface(_))
    }

    pub fn is_object_or_interface(self) -> bool {
        matches!(self, Type::Object(_) | Type::Interface(_))
    }

    pub fn is_union(self) -> bool {
        matches!(self, Type::Union(_))
    }

    pub fn get_enum_id(self) -> Option<EnumID> {
        match self {
            Type::Enum(id) => Some(id),
            _ => None,
        }
    }

    pub fn get_input_object_id(self) -> Option<InputObjectID> {
        match self {
            Type::InputObject(id) => Some(id),
            _ => None,
        }
    }

    pub fn get_interface_id(self) -> Option<InterfaceID> {
        match self {
            Type::Interface(id) => Some(id),
            _ => None,
        }
    }

    pub fn get_object_id(self) -> Option<ObjectID> {
        match self {
            Type::Object(id) => Some(id),
            _ => None,
        }
    }

    pub fn get_scalar_id(self) -> Option<ScalarID> {
        match self {
            Type::Scalar(id) => Some(id),
            _ => None,
        }
    }

    pub fn get_union_id(self) -> Option<UnionID> {
        match self {
            Type::Union(id) => Some(id),
            _ => None,
        }
    }
}

impl fmt::Debug for Type {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Type::Enum(id) => f.write_fmt(format_args!("Enum({:?})", id.0)),
            Type::InputObject(id) => f.write_fmt(format_args!("InputObject({:?})", id.0)),
            Type::Interface(id) => f.write_fmt(format_args!("Interface({:?})", id.0)),
            Type::Object(id) => f.write_fmt(format_args!("Object({:?})", id.0)),
            Type::Scalar(id) => f.write_fmt(format_args!("Scalar({:?})", id.0)),
            Type::Union(id) => f.write_fmt(format_args!("Union({:?})", id.0)),
        }
    }
}

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum TypeReference {
    Named(Type),
    NonNull(Box<TypeReference>),
    List(Box<TypeReference>),
}

impl TypeReference {
    pub fn inner(&self) -> Type {
        match self {
            TypeReference::Named(type_) => *type_,
            TypeReference::List(of) => of.inner(),
            TypeReference::NonNull(of) => of.inner(),
        }
    }

    pub fn non_null(&self) -> TypeReference {
        match self {
            TypeReference::Named(_) => TypeReference::NonNull(Box::new(self.clone())),
            TypeReference::List(_) => TypeReference::NonNull(Box::new(self.clone())),
            TypeReference::NonNull(_) => self.clone(),
        }
    }

    pub fn nullable_type(&self) -> &TypeReference {
        match self {
            TypeReference::Named(_) => self,
            TypeReference::List(_) => self,
            TypeReference::NonNull(of) => of,
        }
    }

    pub fn is_non_null(&self) -> bool {
        matches!(self, TypeReference::NonNull(_))
    }

    pub fn is_list(&self) -> bool {
        matches!(self.nullable_type(), TypeReference::List(_))
    }

    // If the type is Named or NonNull<Named> return the inner named.
    // If the type is a List or NonNull<List> returns a matching list with nullable items.
    pub fn with_nullable_item_type(&self) -> TypeReference {
        match self {
            TypeReference::Named(_) => self.clone(),
            TypeReference::List(of) => TypeReference::List(Box::new(of.nullable_type().clone())),
            TypeReference::NonNull(of) => {
                let inner: &TypeReference = of;
                match inner {
                    TypeReference::List(_) => {
                        TypeReference::NonNull(Box::new(of.with_nullable_item_type()))
                    }
                    TypeReference::Named(_) => inner.clone(),
                    TypeReference::NonNull(_) => {
                        unreachable!("Invalid nested TypeReference::NonNull")
                    }
                }
            }
        }
    }

    pub fn list_item_type(&self) -> Option<&TypeReference> {
        match self.nullable_type() {
            TypeReference::List(of) => Some(of),
            _ => None,
        }
    }

    // Return None if the type is a List, otherwise return the inner type
    pub fn non_list_type(&self) -> Option<Type> {
        match self {
            TypeReference::List(_) => None,
            TypeReference::Named(type_) => Some(*type_),
            TypeReference::NonNull(of) => of.non_list_type(),
        }
    }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Directive {
    pub name: DirectiveName,
    pub arguments: ArgumentDefinitions,
    pub locations: Vec<DirectiveLocation>,
    pub repeatable: bool,
    pub is_extension: bool,
    pub description: Option<StringKey>,
}

impl Named for Directive {
    type Name = DirectiveName;
    fn name(&self) -> DirectiveName {
        self.name
    }
}

#[derive(Clone, Debug)]
pub struct Scalar {
    pub name: WithLocation<StringKey>,
    pub is_extension: bool,
    pub directives: Vec<DirectiveValue>,
    pub description: Option<StringKey>,
}

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct Object {
    pub name: WithLocation<StringKey>,
    pub is_extension: bool,
    pub fields: Vec<FieldID>,
    pub interfaces: Vec<InterfaceID>,
    pub directives: Vec<DirectiveValue>,
    pub description: Option<StringKey>,
}

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct InputObject {
    pub name: WithLocation<StringKey>,
    pub fields: ArgumentDefinitions,
    pub directives: Vec<DirectiveValue>,
    pub description: Option<StringKey>,
}

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct Enum {
    pub name: WithLocation<StringKey>,
    pub is_extension: bool,
    pub values: Vec<EnumValue>,
    pub directives: Vec<DirectiveValue>,
    pub description: Option<StringKey>,
}

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct Union {
    pub name: WithLocation<StringKey>,
    pub is_extension: bool,
    pub members: Vec<ObjectID>,
    pub directives: Vec<DirectiveValue>,
    pub description: Option<StringKey>,
}

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct Interface {
    pub name: WithLocation<StringKey>,
    pub is_extension: bool,
    pub implementing_interfaces: Vec<InterfaceID>,
    pub implementing_objects: Vec<ObjectID>,
    pub fields: Vec<FieldID>,
    pub directives: Vec<DirectiveValue>,
    pub interfaces: Vec<InterfaceID>,
    pub description: Option<StringKey>,
}

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct Field {
    pub name: WithLocation<StringKey>,
    pub is_extension: bool,
    pub arguments: ArgumentDefinitions,
    pub type_: TypeReference,
    pub directives: Vec<DirectiveValue>,
    /// The type on which this field was defined. This field is (should)
    /// always be set, except for special fields such as __typename and
    /// __id, which are queryable on all types and therefore don't have
    /// a single parent type.
    pub parent_type: Option<Type>,
    pub description: Option<StringKey>,
}

pub struct Deprecation {
    pub reason: Option<StringKey>,
}

impl Field {
    pub fn deprecated(&self) -> Option<Deprecation> {
        self.directives
            .named(*DIRECTIVE_DEPRECATED)
            .map(|directive| Deprecation {
                reason: directive
                    .arguments
                    .named(*ARGUMENT_REASON)
                    .and_then(|reason| reason.value.get_string_literal()),
            })
    }
}

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Argument {
    pub name: ArgumentName,
    pub type_: TypeReference,
    pub default_value: Option<ConstantValue>,
    pub description: Option<StringKey>,
    pub directives: Vec<DirectiveValue>,
}

impl Named for Argument {
    type Name = ArgumentName;
    fn name(&self) -> ArgumentName {
        self.name
    }
}

#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct ArgumentValue {
    pub name: ArgumentName,
    pub value: ConstantValue,
}

#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct DirectiveValue {
    pub name: DirectiveName,
    pub arguments: Vec<ArgumentValue>,
}

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct EnumValue {
    pub value: StringKey,
    pub directives: Vec<DirectiveValue>,
}

#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct ArgumentDefinitions(pub(crate) Vec<Argument>);

impl ArgumentDefinitions {
    pub fn new(arguments: Vec<Argument>) -> Self {
        Self(arguments)
    }

    pub fn named(&self, name: ArgumentName) -> Option<&Argument> {
        self.0.named(name)
    }

    pub fn contains(&self, name: StringKey) -> bool {
        self.0.iter().any(|x| x.name == ArgumentName(name))
    }

    pub fn iter(&self) -> Iter<'_, Argument> {
        self.0.iter()
    }

    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }
}

impl fmt::Debug for ArgumentDefinitions {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_fmt(format_args!("{:#?}", self.0))
    }
}

impl IntoIterator for ArgumentDefinitions {
    type Item = Argument;
    type IntoIter = std::vec::IntoIter<Self::Item>;

    fn into_iter(self) -> Self::IntoIter {
        self.0.into_iter()
    }
}

pub trait TypeWithFields {
    fn fields(&self) -> &Vec<FieldID>;
    fn interfaces(&self) -> &Vec<InterfaceID>;
}

impl TypeWithFields for Interface {
    fn fields(&self) -> &Vec<FieldID> {
        &self.fields
    }

    fn interfaces(&self) -> &Vec<InterfaceID> {
        &self.interfaces
    }
}

impl TypeWithFields for Object {
    fn fields(&self) -> &Vec<FieldID> {
        &self.fields
    }

    fn interfaces(&self) -> &Vec<InterfaceID> {
        &self.interfaces
    }
}

#[allow(unused_macros)]
macro_rules! impl_named {
    ($type_name:ident) => {
        impl Named for $type_name {
            type Name = StringKey;
            fn name(&self) -> StringKey {
                self.name
            }
        }
    };
}

macro_rules! impl_named_for_with_location {
    ($type_name:ident) => {
        impl Named for $type_name {
            type Name = StringKey;
            fn name(&self) -> StringKey {
                self.name.item
            }
        }
    };
}

impl_named_for_with_location!(Object);
impl_named_for_with_location!(Field);
impl_named_for_with_location!(InputObject);
impl_named_for_with_location!(Interface);
impl_named_for_with_location!(Union);
impl_named_for_with_location!(Scalar);
impl_named_for_with_location!(Enum);

impl Named for DirectiveValue {
    type Name = DirectiveName;
    fn name(&self) -> DirectiveName {
        self.name
    }
}

impl Named for ArgumentValue {
    type Name = ArgumentName;
    fn name(&self) -> ArgumentName {
        self.name
    }
}
<|endoftext|>


<commit_before>use std::cmp;
use std::io::Read;
use std::io::BufRead;
use std::io::BufReader;
use std::mem;
use std::u64;

#[cfg(feature = "bytes")]
use bytes::Bytes;
#[cfg(feature = "bytes")]
use bytes::BytesMut;
#[cfg(feature = "bytes")]
use bytes::BufMut;

use ProtobufResult;
use ProtobufError;
use error::WireError;

// If an input stream is constructed with a `Read`, we create a
// `BufReader` with an internal buffer of this size.
const INPUT_STREAM_BUFFER_SIZE: usize = 4096;

const USE_UNSAFE_FOR_SPEED: bool = true;

const NO_LIMIT: u64 = u64::MAX;

/// Hold all possible combinations of input source
enum InputSource<'a> {
    BufRead(&'a mut BufRead),
    Read(BufReader<&'a mut Read>),
    Slice(&'a [u8]),
    #[cfg(feature = "bytes")]
    Bytes(&'a Bytes),
}

/// Dangerous implementation of `BufRead`.
///
/// Unsafe wrapper around BufRead which assumes that `BufRead` buf is
/// not moved when `BufRead` is moved.
///
/// This assumption is generally incorrect, however, in practice
/// `BufReadIter` is created either from `BufRead` reference (which
/// cannot  be moved, because it is locked by `CodedInputStream`) or from
/// `BufReader` which does not move its buffer (we know that from
/// inspecting rust standard library).
///
/// It is important for `CodedInputStream` performance that small reads
/// (e. g. 4 bytes reads) do not involve virtual calls or switches.
/// This is achievable with `BufReadIter`.
pub struct BufReadIter<'a> {
    input_source: InputSource<'a>,
    buf: &'a [u8],
    pos_within_buf: usize,
    limit_within_buf: usize,
    pos_of_buf_start: u64,
    limit: u64,
}

impl<'a> Drop for BufReadIter<'a> {
    fn drop(&mut self) {
        match self.input_source {
            InputSource::BufRead(ref mut buf_read) => buf_read.consume(self.pos_within_buf),
            InputSource::Read(_) => {
                // Nothing to flush, because we own BufReader
            }
            _ => {}
        }
    }
}

impl<'ignore> BufReadIter<'ignore> {
    pub fn from_read<'a>(read: &'a mut Read) -> BufReadIter<'a> {
        BufReadIter {
            input_source: InputSource::Read(
                BufReader::with_capacity(INPUT_STREAM_BUFFER_SIZE, read),
            ),
            buf: &[],
            pos_within_buf: 0,
            limit_within_buf: 0,
            pos_of_buf_start: 0,
            limit: NO_LIMIT,
        }
    }

    pub fn from_buf_read<'a>(buf_read: &'a mut BufRead) -> BufReadIter<'a> {
        BufReadIter {
            input_source: InputSource::BufRead(buf_read),
            buf: &[],
            pos_within_buf: 0,
            limit_within_buf: 0,
            pos_of_buf_start: 0,
            limit: NO_LIMIT,
        }
    }

    pub fn from_byte_slice<'a>(bytes: &'a [u8]) -> BufReadIter<'a> {
        BufReadIter {
            input_source: InputSource::Slice(bytes),
            buf: bytes,
            pos_within_buf: 0,
            limit_within_buf: bytes.len(),
            pos_of_buf_start: 0,
            limit: NO_LIMIT,
        }
    }

    #[cfg(feature = "bytes")]
    pub fn from_bytes<'a>(bytes: &'a Bytes) -> BufReadIter<'a> {
        BufReadIter {
            input_source: InputSource::Bytes(bytes),
            buf: &bytes,
            pos_within_buf: 0,
            limit_within_buf: bytes.len(),
            pos_of_buf_start: 0,
            limit: NO_LIMIT,
        }
    }

    #[inline]
    fn assertions(&self) {
        debug_assert!(self.pos_within_buf <= self.limit_within_buf);
        debug_assert!(self.limit_within_buf <= self.buf.len());
        debug_assert!(self.pos_of_buf_start + self.pos_within_buf as u64 <= self.limit);
    }

    pub fn pos(&self) -> u64 {
        self.pos_of_buf_start + self.pos_within_buf as u64
    }

    /// Recompute `limit_within_buf` after update of `limit`
    #[inline]
    fn update_limit_within_buf(&mut self) {
        if self.pos_of_buf_start + (self.buf.len() as u64) <= self.limit {
            self.limit_within_buf = self.buf.len();
        } else {
            self.limit_within_buf = (self.limit - self.pos_of_buf_start) as usize;
        }

        self.assertions();
    }

    pub fn push_limit(&mut self, limit: u64) -> ProtobufResult<u64> {
        // TODO: return error instead of panic
        let new_limit = match self.pos().checked_add(limit) {
            Some(new_limit) => new_limit,
            None => return Err(ProtobufError::WireError(WireError::Other)),
        };

        if new_limit > self.limit {
            return Err(ProtobufError::WireError(WireError::Other));
        }

        let prev_limit = mem::replace(&mut self.limit, new_limit);

        self.update_limit_within_buf();

        Ok(prev_limit)
    }

    #[inline]
    pub fn pop_limit(&mut self, limit: u64) {
        assert!(limit >= self.limit);

        self.limit = limit;

        self.update_limit_within_buf();
    }

    #[inline]
    pub fn remaining_in_buf(&self) -> &[u8] {
        if USE_UNSAFE_FOR_SPEED {
            unsafe {
                &self.buf
                    .get_unchecked(self.pos_within_buf..self.limit_within_buf)
            }
        } else {
            &self.buf[self.pos_within_buf..self.limit_within_buf]
        }
    }

    #[inline(always)]
    pub fn remaining_in_buf_len(&self) -> usize {
        self.limit_within_buf - self.pos_within_buf
    }

    pub fn bytes_until_limit(&self) -> u64 {
        if self.limit == NO_LIMIT {
            NO_LIMIT
        } else {
            self.limit - (self.pos_of_buf_start + self.pos_within_buf as u64)
        }
    }

    #[inline(always)]
    pub fn eof(&mut self) -> ProtobufResult<bool> {
        if self.pos_within_buf == self.limit_within_buf {
            Ok(self.fill_buf()?.is_empty())
        } else {
            Ok(false)
        }
    }

    #[inline(always)]
    pub fn read_byte(&mut self) -> ProtobufResult<u8> {
        if self.pos_within_buf == self.limit_within_buf {
            self.do_fill_buf()?;
            if self.remaining_in_buf_len() == 0 {
                return Err(ProtobufError::WireError(WireError::UnexpectedEof));
            }
        }

        let r = if USE_UNSAFE_FOR_SPEED {
            unsafe { *self.buf.get_unchecked(self.pos_within_buf) }
        } else {
            self.buf[self.pos_within_buf]
        };
        self.pos_within_buf += 1;
        Ok(r)
    }

    #[cfg(feature = "bytes")]
    pub fn read_exact_bytes(&mut self, len: usize) -> ProtobufResult<Bytes> {
        if let InputSource::Bytes(bytes) = self.input_source {
            if self.pos_within_buf + len > self.limit_within_buf {
                return Err(ProtobufError::WireError(WireError::UnexpectedEof));
            }
            let r = bytes.slice(self.pos_within_buf, self.pos_within_buf + len);
            self.pos_within_buf += len;
            Ok(r)
        } else {
            let mut r = BytesMut::with_capacity(len);
            unsafe {
                {
                    let mut buf = &mut r.bytes_mut()[..len];
                    self.read_exact(buf)?;
                }
                r.advance_mut(len);
            }
            Ok(r.freeze())
        }
    }

    pub fn read(&mut self, buf: &mut [u8]) -> ProtobufResult<usize> {
        self.fill_buf()?;

        let rem = &self.buf[self.pos_within_buf..self.limit_within_buf];

        let len = cmp::min(rem.len(), buf.len());
        &mut buf[..len].copy_from_slice(&rem[..len]);
        self.pos_within_buf += len;
        Ok((len))
    }

    pub fn read_exact(&mut self, mut buf: &mut [u8]) -> ProtobufResult<()> {
        if self.remaining_in_buf_len() >= buf.len() {
            let buf_len = buf.len();
            buf.copy_from_slice(
                &self.buf[self.pos_within_buf..self.pos_within_buf + buf_len],
            );
            self.pos_within_buf += buf_len;
            return Ok(());
        }

        if self.bytes_until_limit() < buf.len() as u64 {
            return Err(ProtobufError::WireError(WireError::UnexpectedEof));
        }

        let consume = self.pos_within_buf;
        self.pos_of_buf_start += self.pos_within_buf as u64;
        self.pos_within_buf = 0;
        self.buf = &[];
        self.limit_within_buf = 0;

        match self.input_source {
            InputSource::Read(ref mut buf_read) => {
                buf_read.consume(consume);
                buf_read.read_exact(buf)?;
            }
            InputSource::BufRead(ref mut buf_read) => {
                buf_read.consume(consume);
                buf_read.read_exact(buf)?;
            }
            _ => {
                return Err(ProtobufError::WireError(WireError::UnexpectedEof));
            }
        }

        self.pos_of_buf_start += buf.len() as u64;

        self.assertions();

        Ok(())
    }

    fn do_fill_buf(&mut self) -> ProtobufResult<()> {
        debug_assert!(self.pos_within_buf == self.limit_within_buf);

        // Limit is reached, so EOF is reached
        if self.limit_within_buf != self.buf.len() {
            return Ok(());
        }

        let consume = self.buf.len();
        self.pos_of_buf_start += self.buf.len() as u64;
        self.buf = &[];
        self.pos_within_buf = 0;
        self.limit_within_buf = 0;

        match self.input_source {
            InputSource::Read(ref mut buf_read) => {
                buf_read.consume(consume);
                self.buf = unsafe { mem::transmute(buf_read.fill_buf()?) };
            }
            InputSource::BufRead(ref mut buf_read) => {
                buf_read.consume(consume);
                self.buf = unsafe { mem::transmute(buf_read.fill_buf()?) };
            }
            _ => {
                return Ok(());
            }
        }

        self.update_limit_within_buf();

        Ok(())
    }

    #[inline(always)]
    pub fn fill_buf(&mut self) -> ProtobufResult<&[u8]> {
        if self.pos_within_buf == self.limit_within_buf {
            self.do_fill_buf()?;
        }

        Ok(if USE_UNSAFE_FOR_SPEED {
            unsafe {
                self.buf
                    .get_unchecked(self.pos_within_buf..self.limit_within_buf)
            }
        } else {
            &self.buf[self.pos_within_buf..self.limit_within_buf]
        })
    }

    #[inline(always)]
    pub fn consume(&mut self, amt: usize) {
        assert!(amt <= self.limit_within_buf - self.pos_within_buf);
        self.pos_within_buf += amt;
    }
}

#[cfg(all(test, feature = "bytes"))]
mod test_bytes {
    use super::*;
    use std::io::Write;

    fn make_long_string(len: usize) -> Vec<u8> {
        let mut s = Vec::new();
        while s.len() < len {
            let len = s.len();
            write!(&mut s, "{}", len).expect("unexpected");
        }
        s.truncate(len);
        s
    }

    #[test]
    fn read_exact_bytes_from_slice() {
        let bytes = make_long_string(100);
        let mut bri = BufReadIter::from_byte_slice(&bytes[..]);
        assert_eq!(&bytes[..90], &bri.read_exact_bytes(90).unwrap()[..]);
        assert_eq!(bytes[90], bri.read_byte().expect("read_byte"));
    }

    #[test]
    fn read_exact_bytes_from_bytes() {
        let bytes = Bytes::from(make_long_string(100));
        let mut bri = BufReadIter::from_bytes(&bytes);
        let read = bri.read_exact_bytes(90).unwrap();
        assert_eq!(&bytes[..90], &read[..]);
        assert_eq!(&bytes[..90].as_ptr(), &read.as_ptr());
        assert_eq!(bytes[90], bri.read_byte().expect("read_byte"));
    }

}
<commit_msg>Do not fill_buf when BufReadIter.pos == limit<commit_after>use std::cmp;
use std::io::Read;
use std::io::BufRead;
use std::io::BufReader;
use std::mem;
use std::u64;

#[cfg(feature = "bytes")]
use bytes::Bytes;
#[cfg(feature = "bytes")]
use bytes::BytesMut;
#[cfg(feature = "bytes")]
use bytes::BufMut;

use ProtobufResult;
use ProtobufError;
use error::WireError;

// If an input stream is constructed with a `Read`, we create a
// `BufReader` with an internal buffer of this size.
const INPUT_STREAM_BUFFER_SIZE: usize = 4096;

const USE_UNSAFE_FOR_SPEED: bool = true;

const NO_LIMIT: u64 = u64::MAX;

/// Hold all possible combinations of input source
enum InputSource<'a> {
    BufRead(&'a mut BufRead),
    Read(BufReader<&'a mut Read>),
    Slice(&'a [u8]),
    #[cfg(feature = "bytes")]
    Bytes(&'a Bytes),
}

/// Dangerous implementation of `BufRead`.
///
/// Unsafe wrapper around BufRead which assumes that `BufRead` buf is
/// not moved when `BufRead` is moved.
///
/// This assumption is generally incorrect, however, in practice
/// `BufReadIter` is created either from `BufRead` reference (which
/// cannot  be moved, because it is locked by `CodedInputStream`) or from
/// `BufReader` which does not move its buffer (we know that from
/// inspecting rust standard library).
///
/// It is important for `CodedInputStream` performance that small reads
/// (e. g. 4 bytes reads) do not involve virtual calls or switches.
/// This is achievable with `BufReadIter`.
pub struct BufReadIter<'a> {
    input_source: InputSource<'a>,
    buf: &'a [u8],
    pos_within_buf: usize,
    limit_within_buf: usize,
    pos_of_buf_start: u64,
    limit: u64,
}

impl<'a> Drop for BufReadIter<'a> {
    fn drop(&mut self) {
        match self.input_source {
            InputSource::BufRead(ref mut buf_read) => buf_read.consume(self.pos_within_buf),
            InputSource::Read(_) => {
                // Nothing to flush, because we own BufReader
            }
            _ => {}
        }
    }
}

impl<'ignore> BufReadIter<'ignore> {
    pub fn from_read<'a>(read: &'a mut Read) -> BufReadIter<'a> {
        BufReadIter {
            input_source: InputSource::Read(
                BufReader::with_capacity(INPUT_STREAM_BUFFER_SIZE, read),
            ),
            buf: &[],
            pos_within_buf: 0,
            limit_within_buf: 0,
            pos_of_buf_start: 0,
            limit: NO_LIMIT,
        }
    }

    pub fn from_buf_read<'a>(buf_read: &'a mut BufRead) -> BufReadIter<'a> {
        BufReadIter {
            input_source: InputSource::BufRead(buf_read),
            buf: &[],
            pos_within_buf: 0,
            limit_within_buf: 0,
            pos_of_buf_start: 0,
            limit: NO_LIMIT,
        }
    }

    pub fn from_byte_slice<'a>(bytes: &'a [u8]) -> BufReadIter<'a> {
        BufReadIter {
            input_source: InputSource::Slice(bytes),
            buf: bytes,
            pos_within_buf: 0,
            limit_within_buf: bytes.len(),
            pos_of_buf_start: 0,
            limit: NO_LIMIT,
        }
    }

    #[cfg(feature = "bytes")]
    pub fn from_bytes<'a>(bytes: &'a Bytes) -> BufReadIter<'a> {
        BufReadIter {
            input_source: InputSource::Bytes(bytes),
            buf: &bytes,
            pos_within_buf: 0,
            limit_within_buf: bytes.len(),
            pos_of_buf_start: 0,
            limit: NO_LIMIT,
        }
    }

    #[inline]
    fn assertions(&self) {
        debug_assert!(self.pos_within_buf <= self.limit_within_buf);
        debug_assert!(self.limit_within_buf <= self.buf.len());
        debug_assert!(self.pos_of_buf_start + self.pos_within_buf as u64 <= self.limit);
    }

    #[inline(always)]
    pub fn pos(&self) -> u64 {
        self.pos_of_buf_start + self.pos_within_buf as u64
    }

    /// Recompute `limit_within_buf` after update of `limit`
    #[inline]
    fn update_limit_within_buf(&mut self) {
        if self.pos_of_buf_start + (self.buf.len() as u64) <= self.limit {
            self.limit_within_buf = self.buf.len();
        } else {
            self.limit_within_buf = (self.limit - self.pos_of_buf_start) as usize;
        }

        self.assertions();
    }

    pub fn push_limit(&mut self, limit: u64) -> ProtobufResult<u64> {
        // TODO: return error instead of panic
        let new_limit = match self.pos().checked_add(limit) {
            Some(new_limit) => new_limit,
            None => return Err(ProtobufError::WireError(WireError::Other)),
        };

        if new_limit > self.limit {
            return Err(ProtobufError::WireError(WireError::Other));
        }

        let prev_limit = mem::replace(&mut self.limit, new_limit);

        self.update_limit_within_buf();

        Ok(prev_limit)
    }

    #[inline]
    pub fn pop_limit(&mut self, limit: u64) {
        assert!(limit >= self.limit);

        self.limit = limit;

        self.update_limit_within_buf();
    }

    #[inline]
    pub fn remaining_in_buf(&self) -> &[u8] {
        if USE_UNSAFE_FOR_SPEED {
            unsafe {
                &self.buf
                    .get_unchecked(self.pos_within_buf..self.limit_within_buf)
            }
        } else {
            &self.buf[self.pos_within_buf..self.limit_within_buf]
        }
    }

    #[inline(always)]
    pub fn remaining_in_buf_len(&self) -> usize {
        self.limit_within_buf - self.pos_within_buf
    }

    pub fn bytes_until_limit(&self) -> u64 {
        if self.limit == NO_LIMIT {
            NO_LIMIT
        } else {
            self.limit - (self.pos_of_buf_start + self.pos_within_buf as u64)
        }
    }

    #[inline(always)]
    pub fn eof(&mut self) -> ProtobufResult<bool> {
        if self.pos_within_buf == self.limit_within_buf {
            Ok(self.fill_buf()?.is_empty())
        } else {
            Ok(false)
        }
    }

    #[inline(always)]
    pub fn read_byte(&mut self) -> ProtobufResult<u8> {
        if self.pos_within_buf == self.limit_within_buf {
            self.do_fill_buf()?;
            if self.remaining_in_buf_len() == 0 {
                return Err(ProtobufError::WireError(WireError::UnexpectedEof));
            }
        }

        let r = if USE_UNSAFE_FOR_SPEED {
            unsafe { *self.buf.get_unchecked(self.pos_within_buf) }
        } else {
            self.buf[self.pos_within_buf]
        };
        self.pos_within_buf += 1;
        Ok(r)
    }

    #[cfg(feature = "bytes")]
    pub fn read_exact_bytes(&mut self, len: usize) -> ProtobufResult<Bytes> {
        if let InputSource::Bytes(bytes) = self.input_source {
            if self.pos_within_buf + len > self.limit_within_buf {
                return Err(ProtobufError::WireError(WireError::UnexpectedEof));
            }
            let r = bytes.slice(self.pos_within_buf, self.pos_within_buf + len);
            self.pos_within_buf += len;
            Ok(r)
        } else {
            let mut r = BytesMut::with_capacity(len);
            unsafe {
                {
                    let mut buf = &mut r.bytes_mut()[..len];
                    self.read_exact(buf)?;
                }
                r.advance_mut(len);
            }
            Ok(r.freeze())
        }
    }

    pub fn read(&mut self, buf: &mut [u8]) -> ProtobufResult<usize> {
        self.fill_buf()?;

        let rem = &self.buf[self.pos_within_buf..self.limit_within_buf];

        let len = cmp::min(rem.len(), buf.len());
        &mut buf[..len].copy_from_slice(&rem[..len]);
        self.pos_within_buf += len;
        Ok((len))
    }

    pub fn read_exact(&mut self, mut buf: &mut [u8]) -> ProtobufResult<()> {
        if self.remaining_in_buf_len() >= buf.len() {
            let buf_len = buf.len();
            buf.copy_from_slice(
                &self.buf[self.pos_within_buf..self.pos_within_buf + buf_len],
            );
            self.pos_within_buf += buf_len;
            return Ok(());
        }

        if self.bytes_until_limit() < buf.len() as u64 {
            return Err(ProtobufError::WireError(WireError::UnexpectedEof));
        }

        let consume = self.pos_within_buf;
        self.pos_of_buf_start += self.pos_within_buf as u64;
        self.pos_within_buf = 0;
        self.buf = &[];
        self.limit_within_buf = 0;

        match self.input_source {
            InputSource::Read(ref mut buf_read) => {
                buf_read.consume(consume);
                buf_read.read_exact(buf)?;
            }
            InputSource::BufRead(ref mut buf_read) => {
                buf_read.consume(consume);
                buf_read.read_exact(buf)?;
            }
            _ => {
                return Err(ProtobufError::WireError(WireError::UnexpectedEof));
            }
        }

        self.pos_of_buf_start += buf.len() as u64;

        self.assertions();

        Ok(())
    }

    fn do_fill_buf(&mut self) -> ProtobufResult<()> {
        debug_assert!(self.pos_within_buf == self.limit_within_buf);

        // Limit is reached, do not fill buf, because otherwise
        // synchronous read from `CodedInputStream` may block.
        if self.limit == self.pos() {
            return Ok(());
        }

        let consume = self.buf.len();
        self.pos_of_buf_start += self.buf.len() as u64;
        self.buf = &[];
        self.pos_within_buf = 0;
        self.limit_within_buf = 0;

        match self.input_source {
            InputSource::Read(ref mut buf_read) => {
                buf_read.consume(consume);
                self.buf = unsafe { mem::transmute(buf_read.fill_buf()?) };
            }
            InputSource::BufRead(ref mut buf_read) => {
                buf_read.consume(consume);
                self.buf = unsafe { mem::transmute(buf_read.fill_buf()?) };
            }
            _ => {
                return Ok(());
            }
        }

        self.update_limit_within_buf();

        Ok(())
    }

    #[inline(always)]
    pub fn fill_buf(&mut self) -> ProtobufResult<&[u8]> {
        if self.pos_within_buf == self.limit_within_buf {
            self.do_fill_buf()?;
        }

        Ok(if USE_UNSAFE_FOR_SPEED {
            unsafe {
                self.buf
                    .get_unchecked(self.pos_within_buf..self.limit_within_buf)
            }
        } else {
            &self.buf[self.pos_within_buf..self.limit_within_buf]
        })
    }

    #[inline(always)]
    pub fn consume(&mut self, amt: usize) {
        assert!(amt <= self.limit_within_buf - self.pos_within_buf);
        self.pos_within_buf += amt;
    }
}

#[cfg(all(test, feature = "bytes"))]
mod test_bytes {
    use super::*;
    use std::io::Write;

    fn make_long_string(len: usize) -> Vec<u8> {
        let mut s = Vec::new();
        while s.len() < len {
            let len = s.len();
            write!(&mut s, "{}", len).expect("unexpected");
        }
        s.truncate(len);
        s
    }

    #[test]
    fn read_exact_bytes_from_slice() {
        let bytes = make_long_string(100);
        let mut bri = BufReadIter::from_byte_slice(&bytes[..]);
        assert_eq!(&bytes[..90], &bri.read_exact_bytes(90).unwrap()[..]);
        assert_eq!(bytes[90], bri.read_byte().expect("read_byte"));
    }

    #[test]
    fn read_exact_bytes_from_bytes() {
        let bytes = Bytes::from(make_long_string(100));
        let mut bri = BufReadIter::from_bytes(&bytes);
        let read = bri.read_exact_bytes(90).unwrap();
        assert_eq!(&bytes[..90], &read[..]);
        assert_eq!(&bytes[..90].as_ptr(), &read.as_ptr());
        assert_eq!(bytes[90], bri.read_byte().expect("read_byte"));
    }

}

#[cfg(test)]
mod test {
    use super::*;
    use std::io::BufRead;
    use std::io::Read;
    use std::io;

    #[test]
    fn eof_at_limit() {
        struct Read5ThenPanic {
            pos: usize,
        }

        impl Read for Read5ThenPanic {
            fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
                unreachable!();
            }
        }

        impl BufRead for Read5ThenPanic {
            fn fill_buf(&mut self) -> io::Result<&[u8]> {
                assert_eq!(0, self.pos);
                static ZERO_TO_FIVE: &'static [u8] = &[0, 1, 2, 3, 4];
                Ok(ZERO_TO_FIVE)
            }

            fn consume(&mut self, amt: usize) {
                if amt == 0 {
                    // drop of BufReadIter
                    return;
                }

                assert_eq!(0, self.pos);
                assert_eq!(5, amt);
                self.pos += amt;
            }
        }

        let mut read = Read5ThenPanic { pos : 0 };
        let mut buf_read_iter = BufReadIter::from_buf_read(&mut read);
        assert_eq!(0, buf_read_iter.pos());
        let _prev_limit = buf_read_iter.push_limit(5);
        buf_read_iter.read_byte().expect("read_byte");
        buf_read_iter.read_exact(&mut [1, 2, 3, 4]).expect("read_exact");
        assert!(buf_read_iter.eof().expect("eof"));
    }
}
<|endoftext|>


<commit_before>use syntax::{
    ast::{self, AstNode},
    SyntaxElement, SyntaxKind, TextRange, TextSize, T,
};

use crate::{AssistContext, AssistId, AssistKind, Assists};

// Assist: remove_dbg
//
// Removes `dbg!()` macro call.
//
// ```
// fn main() {
//     <|>dbg!(92);
// }
// ```
// ->
// ```
// fn main() {
//     92;
// }
// ```
pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let macro_call = ctx.find_node_at_offset::<ast::MacroCall>()?;
    let new_contents = adjusted_macro_contents(&macro_call)?;

    let macro_text_range = macro_call.syntax().text_range();
    let macro_end = if macro_call.semicolon_token().is_some() {
        macro_text_range.end() - TextSize::of(';')
    } else {
        macro_text_range.end()
    };

    acc.add(
        AssistId("remove_dbg", AssistKind::Refactor),
        "Remove dbg!()",
        macro_text_range,
        |builder| {
            builder.replace(TextRange::new(macro_text_range.start(), macro_end), new_contents);
        },
    )
}

fn adjusted_macro_contents(macro_call: &ast::MacroCall) -> Option<String> {
    let contents = get_valid_macrocall_contents(&macro_call, "dbg")?;
    let macro_text_with_brackets = macro_call.token_tree()?.syntax().text();
    let macro_text_in_brackets = macro_text_with_brackets.slice(TextRange::new(
        TextSize::of('('),
        macro_text_with_brackets.len() - TextSize::of(')'),
    ));

    let is_leaf = macro_call.syntax().next_sibling().is_none();
    Some(if !is_leaf && needs_parentheses_around_macro_contents(contents) {
        format!("({})", macro_text_in_brackets)
    } else {
        macro_text_in_brackets.to_string()
    })
}

/// Verifies that the given macro_call actually matches the given name
/// and contains proper ending tokens, then returns the contents between the ending tokens
fn get_valid_macrocall_contents(
    macro_call: &ast::MacroCall,
    macro_name: &str,
) -> Option<Vec<SyntaxElement>> {
    let path = macro_call.path()?;
    let name_ref = path.segment()?.name_ref()?;

    // Make sure it is actually a dbg-macro call, dbg followed by !
    let excl = path.syntax().next_sibling_or_token()?;
    if name_ref.text() != macro_name || excl.kind() != T![!] {
        return None;
    }

    let mut children_with_tokens = macro_call.token_tree()?.syntax().children_with_tokens();
    let first_child = children_with_tokens.next()?;
    let mut contents_between_brackets = children_with_tokens.collect::<Vec<_>>();
    let last_child = contents_between_brackets.pop()?;

    if contents_between_brackets.is_empty() {
        None
    } else {
        match (first_child.kind(), last_child.kind()) {
            (T!['('], T![')']) | (T!['['], T![']']) | (T!['{'], T!['}']) => {
                Some(contents_between_brackets)
            }
            _ => None,
        }
    }
}

fn needs_parentheses_around_macro_contents(macro_contents: Vec<SyntaxElement>) -> bool {
    if macro_contents.len() < 2 {
        return false;
    }
    let mut unpaired_brackets_in_contents = Vec::new();
    for element in macro_contents {
        match element.kind() {
            T!['('] | T!['['] | T!['{'] => unpaired_brackets_in_contents.push(element),
            T![')'] => {
                if !matches!(unpaired_brackets_in_contents.pop(), Some(correct_bracket) if correct_bracket.kind() == T!['('])
                {
                    return true;
                }
            }
            T![']'] => {
                if !matches!(unpaired_brackets_in_contents.pop(), Some(correct_bracket) if correct_bracket.kind() == T!['['])
                {
                    return true;
                }
            }
            T!['}'] => {
                if !matches!(unpaired_brackets_in_contents.pop(), Some(correct_bracket) if correct_bracket.kind() == T!['{'])
                {
                    return true;
                }
            }
            symbol_kind => {
                let symbol_not_in_bracket = unpaired_brackets_in_contents.is_empty();
                if symbol_not_in_bracket
                    && symbol_kind != SyntaxKind::COLON
                    && symbol_kind != SyntaxKind::DOT
                    && symbol_kind.is_punct()
                {
                    return true;
                }
            }
        }
    }
    !unpaired_brackets_in_contents.is_empty()
}

#[cfg(test)]
mod tests {
    use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};

    use super::*;

    #[test]
    fn test_remove_dbg() {
        check_assist(remove_dbg, "<|>dbg!(1 + 1)", "1 + 1");

        check_assist(remove_dbg, "dbg!<|>((1 + 1))", "(1 + 1)");

        check_assist(remove_dbg, "dbg!(1 <|>+ 1)", "1 + 1");

        check_assist(remove_dbg, "let _ = <|>dbg!(1 + 1)", "let _ = 1 + 1");

        check_assist(
            remove_dbg,
            "
fn foo(n: usize) {
    if let Some(_) = dbg!(n.<|>checked_sub(4)) {
        // ...
    }
}
",
            "
fn foo(n: usize) {
    if let Some(_) = n.checked_sub(4) {
        // ...
    }
}
",
        );

        check_assist(remove_dbg, "<|>dbg!(Foo::foo_test()).bar()", "Foo::foo_test().bar()");
    }

    #[test]
    fn test_remove_dbg_with_brackets_and_braces() {
        check_assist(remove_dbg, "dbg![<|>1 + 1]", "1 + 1");
        check_assist(remove_dbg, "dbg!{<|>1 + 1}", "1 + 1");
    }

    #[test]
    fn test_remove_dbg_not_applicable() {
        check_assist_not_applicable(remove_dbg, "<|>vec![1, 2, 3]");
        check_assist_not_applicable(remove_dbg, "<|>dbg(5, 6, 7)");
        check_assist_not_applicable(remove_dbg, "<|>dbg!(5, 6, 7");
    }

    #[test]
    fn test_remove_dbg_target() {
        check_assist_target(
            remove_dbg,
            "
fn foo(n: usize) {
    if let Some(_) = dbg!(n.<|>checked_sub(4)) {
        // ...
    }
}
",
            "dbg!(n.checked_sub(4))",
        );
    }

    #[test]
    fn test_remove_dbg_keep_semicolon() {
        // https://github.com/rust-analyzer/rust-analyzer/issues/5129#issuecomment-651399779
        // not quite though
        // adding a comment at the end of the line makes
        // the ast::MacroCall to include the semicolon at the end
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(1 * 20); // needless comment"#,
            r#"let res = 1 * 20; // needless comment"#,
        );
    }

    #[test]
    fn remove_dbg_from_non_leaf_simple_expression() {
        check_assist(
            remove_dbg,
            "
fn main() {
    let mut a = 1;
    while dbg!<|>(a) < 10000 {
        a += 1;
    }
}
",
            "
fn main() {
    let mut a = 1;
    while a < 10000 {
        a += 1;
    }
}
",
        );
    }

    #[test]
    fn test_remove_dbg_keep_expression() {
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(a + b).foo();"#,
            r#"let res = (a + b).foo();"#,
        );

        check_assist(remove_dbg, r#"let res = <|>dbg!(2 + 2) * 5"#, r#"let res = (2 + 2) * 5"#);
        check_assist(remove_dbg, r#"let res = <|>dbg![2 + 2] * 5"#, r#"let res = (2 + 2) * 5"#);
    }

    #[test]
    fn test_remove_dbg_method_chaining() {
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(foo().bar()).baz();"#,
            r#"let res = foo().bar().baz();"#,
        );
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(foo.bar()).baz();"#,
            r#"let res = foo.bar().baz();"#,
        );
    }

    #[test]
    fn test_remove_dbg_field_chaining() {
        check_assist(remove_dbg, r#"let res = <|>dbg!(foo.bar).baz;"#, r#"let res = foo.bar.baz;"#);
    }

    #[test]
    fn test_remove_dbg_from_inside_fn() {
        check_assist_target(
            remove_dbg,
            r#"
fn square(x: u32) -> u32 {
    x * x
}

fn main() {
    let x = square(dbg<|>!(5 + 10));
    println!("{}", x);
}"#,
            "dbg!(5 + 10)",
        );

        check_assist(
            remove_dbg,
            r#"
fn square(x: u32) -> u32 {
    x * x
}

fn main() {
    let x = square(dbg<|>!(5 + 10));
    println!("{}", x);
}"#,
            r#"
fn square(x: u32) -> u32 {
    x * x
}

fn main() {
    let x = square(5 + 10);
    println!("{}", x);
}"#,
        );
    }
}
<commit_msg>Don't keep parens around in remove-dbg<commit_after>use syntax::{
    ast::{self, AstNode},
    SyntaxElement, SyntaxKind, TextRange, TextSize, T,
};

use crate::{AssistContext, AssistId, AssistKind, Assists};

// Assist: remove_dbg
//
// Removes `dbg!()` macro call.
//
// ```
// fn main() {
//     <|>dbg!(92);
// }
// ```
// ->
// ```
// fn main() {
//     92;
// }
// ```
pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let macro_call = ctx.find_node_at_offset::<ast::MacroCall>()?;
    let new_contents = adjusted_macro_contents(&macro_call)?;

    let macro_text_range = macro_call.syntax().text_range();
    let macro_end = if macro_call.semicolon_token().is_some() {
        macro_text_range.end() - TextSize::of(';')
    } else {
        macro_text_range.end()
    };

    acc.add(
        AssistId("remove_dbg", AssistKind::Refactor),
        "Remove dbg!()",
        macro_text_range,
        |builder| {
            builder.replace(TextRange::new(macro_text_range.start(), macro_end), new_contents);
        },
    )
}

fn adjusted_macro_contents(macro_call: &ast::MacroCall) -> Option<String> {
    let contents = get_valid_macrocall_contents(&macro_call, "dbg")?;
    let macro_text_with_brackets = macro_call.token_tree()?.syntax().text();
    let macro_text_in_brackets = macro_text_with_brackets.slice(TextRange::new(
        TextSize::of('('),
        macro_text_with_brackets.len() - TextSize::of(')'),
    ));

    let is_leaf = macro_call.syntax().next_sibling().is_none();
    Some(if !is_leaf && needs_parentheses_around_macro_contents(contents) {
        format!("({})", macro_text_in_brackets)
    } else {
        macro_text_in_brackets.to_string()
    })
}

/// Verifies that the given macro_call actually matches the given name
/// and contains proper ending tokens, then returns the contents between the ending tokens
fn get_valid_macrocall_contents(
    macro_call: &ast::MacroCall,
    macro_name: &str,
) -> Option<Vec<SyntaxElement>> {
    let path = macro_call.path()?;
    let name_ref = path.segment()?.name_ref()?;

    // Make sure it is actually a dbg-macro call, dbg followed by !
    let excl = path.syntax().next_sibling_or_token()?;
    if name_ref.text() != macro_name || excl.kind() != T![!] {
        return None;
    }

    let mut children_with_tokens = macro_call.token_tree()?.syntax().children_with_tokens();
    let first_child = children_with_tokens.next()?;
    let mut contents_between_brackets = children_with_tokens.collect::<Vec<_>>();
    let last_child = contents_between_brackets.pop()?;

    if contents_between_brackets.is_empty() {
        None
    } else {
        match (first_child.kind(), last_child.kind()) {
            (T!['('], T![')']) | (T!['['], T![']']) | (T!['{'], T!['}']) => {
                Some(contents_between_brackets)
            }
            _ => None,
        }
    }
}

fn needs_parentheses_around_macro_contents(macro_contents: Vec<SyntaxElement>) -> bool {
    if macro_contents.len() < 2 {
        return false;
    }
    let mut unpaired_brackets_in_contents = Vec::new();
    for element in macro_contents {
        match element.kind() {
            T!['('] | T!['['] | T!['{'] => unpaired_brackets_in_contents.push(element),
            T![')'] => {
                if !matches!(unpaired_brackets_in_contents.pop(), Some(correct_bracket) if correct_bracket.kind() == T!['('])
                {
                    return true;
                }
            }
            T![']'] => {
                if !matches!(unpaired_brackets_in_contents.pop(), Some(correct_bracket) if correct_bracket.kind() == T!['['])
                {
                    return true;
                }
            }
            T!['}'] => {
                if !matches!(unpaired_brackets_in_contents.pop(), Some(correct_bracket) if correct_bracket.kind() == T!['{'])
                {
                    return true;
                }
            }
            symbol_kind => {
                let symbol_not_in_bracket = unpaired_brackets_in_contents.is_empty();
                if symbol_not_in_bracket
                    // paths
                    && symbol_kind != SyntaxKind::COLON
                    // field/method access
                    && symbol_kind != SyntaxKind::DOT
                    // try operator
                    && symbol_kind != SyntaxKind::QUESTION
                    && (symbol_kind.is_punct() || symbol_kind == SyntaxKind::AS_KW)
                {
                    return true;
                }
            }
        }
    }
    !unpaired_brackets_in_contents.is_empty()
}

#[cfg(test)]
mod tests {
    use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};

    use super::*;

    #[test]
    fn test_remove_dbg() {
        check_assist(remove_dbg, "<|>dbg!(1 + 1)", "1 + 1");

        check_assist(remove_dbg, "dbg!<|>((1 + 1))", "(1 + 1)");

        check_assist(remove_dbg, "dbg!(1 <|>+ 1)", "1 + 1");

        check_assist(remove_dbg, "let _ = <|>dbg!(1 + 1)", "let _ = 1 + 1");

        check_assist(
            remove_dbg,
            "
fn foo(n: usize) {
    if let Some(_) = dbg!(n.<|>checked_sub(4)) {
        // ...
    }
}
",
            "
fn foo(n: usize) {
    if let Some(_) = n.checked_sub(4) {
        // ...
    }
}
",
        );

        check_assist(remove_dbg, "<|>dbg!(Foo::foo_test()).bar()", "Foo::foo_test().bar()");
    }

    #[test]
    fn test_remove_dbg_with_brackets_and_braces() {
        check_assist(remove_dbg, "dbg![<|>1 + 1]", "1 + 1");
        check_assist(remove_dbg, "dbg!{<|>1 + 1}", "1 + 1");
    }

    #[test]
    fn test_remove_dbg_not_applicable() {
        check_assist_not_applicable(remove_dbg, "<|>vec![1, 2, 3]");
        check_assist_not_applicable(remove_dbg, "<|>dbg(5, 6, 7)");
        check_assist_not_applicable(remove_dbg, "<|>dbg!(5, 6, 7");
    }

    #[test]
    fn test_remove_dbg_target() {
        check_assist_target(
            remove_dbg,
            "
fn foo(n: usize) {
    if let Some(_) = dbg!(n.<|>checked_sub(4)) {
        // ...
    }
}
",
            "dbg!(n.checked_sub(4))",
        );
    }

    #[test]
    fn test_remove_dbg_keep_semicolon() {
        // https://github.com/rust-analyzer/rust-analyzer/issues/5129#issuecomment-651399779
        // not quite though
        // adding a comment at the end of the line makes
        // the ast::MacroCall to include the semicolon at the end
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(1 * 20); // needless comment"#,
            r#"let res = 1 * 20; // needless comment"#,
        );
    }

    #[test]
    fn remove_dbg_from_non_leaf_simple_expression() {
        check_assist(
            remove_dbg,
            "
fn main() {
    let mut a = 1;
    while dbg!<|>(a) < 10000 {
        a += 1;
    }
}
",
            "
fn main() {
    let mut a = 1;
    while a < 10000 {
        a += 1;
    }
}
",
        );
    }

    #[test]
    fn test_remove_dbg_keep_expression() {
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(a + b).foo();"#,
            r#"let res = (a + b).foo();"#,
        );

        check_assist(remove_dbg, r#"let res = <|>dbg!(2 + 2) * 5"#, r#"let res = (2 + 2) * 5"#);
        check_assist(remove_dbg, r#"let res = <|>dbg![2 + 2] * 5"#, r#"let res = (2 + 2) * 5"#);
    }

    #[test]
    fn test_remove_dbg_method_chaining() {
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(foo().bar()).baz();"#,
            r#"let res = foo().bar().baz();"#,
        );
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(foo.bar()).baz();"#,
            r#"let res = foo.bar().baz();"#,
        );
    }

    #[test]
    fn test_remove_dbg_field_chaining() {
        check_assist(remove_dbg, r#"let res = <|>dbg!(foo.bar).baz;"#, r#"let res = foo.bar.baz;"#);
    }

    #[test]
    fn test_remove_dbg_from_inside_fn() {
        check_assist_target(
            remove_dbg,
            r#"
fn square(x: u32) -> u32 {
    x * x
}

fn main() {
    let x = square(dbg<|>!(5 + 10));
    println!("{}", x);
}"#,
            "dbg!(5 + 10)",
        );

        check_assist(
            remove_dbg,
            r#"
fn square(x: u32) -> u32 {
    x * x
}

fn main() {
    let x = square(dbg<|>!(5 + 10));
    println!("{}", x);
}"#,
            r#"
fn square(x: u32) -> u32 {
    x * x
}

fn main() {
    let x = square(5 + 10);
    println!("{}", x);
}"#,
        );
    }

    #[test]
    fn test_remove_dbg_try_expr() {
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(result?).foo();"#,
            r#"let res = result?.foo();"#,
        );
    }

    #[test]
    fn test_remove_dbg_await_expr() {
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(fut.await).foo();"#,
            r#"let res = fut.await.foo();"#,
        );
    }

    #[test]
    fn test_remove_dbg_as_cast() {
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(3 as usize).foo();"#,
            r#"let res = (3 as usize).foo();"#,
        );
    }

    #[test]
    fn test_remove_dbg_index_expr() {
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(array[3]).foo();"#,
            r#"let res = array[3].foo();"#,
        );
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(tuple.3).foo();"#,
            r#"let res = tuple.3.foo();"#,
        );
    }

    #[test]
    #[ignore] // FIXME: we encounter SyntaxKind::DOT instead of SyntaxKind::DOT2 causing this to fail
    fn test_remove_dbg_range_expr() {
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(foo..bar).foo();"#,
            r#"let res = (foo..bar).foo();"#,
        );
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(foo..=bar).foo();"#,
            r#"let res = (foo..=bar).foo();"#,
        );
    }
}
<|endoftext|>


<commit_before>use anyhow::{bail, Result};
use std::process::{Child, Command};

use crate::task_handler::ProcessAction;
use log::info;

pub fn compile_shell_command(command_string: &str) -> Command {
    // Chain two `powershell` commands, one that sets the output encoding to utf8 and then the user provided one.
    let mut command = Command::new("powershell");
    command.arg("-c").arg(format!(
        "[Console]::OutputEncoding = [Text.UTF8Encoding]::UTF8; {}",
        command_string
    ));

    command
}

/// Send a signal to a windows process.
pub fn send_signal_to_child(
    _child: &Child,
    _action: &ProcessAction,
    _children: bool,
) -> Result<bool> {
    bail!("not supported on windows.")
}

/// Kill a child process
pub fn kill_child(task_id: usize, child: &mut Child, _kill_children: bool) -> bool {
    match child.kill() {
        Err(_) => {
            info!("Task {} has already finished by itself", task_id);
            false
        }
        Ok(_) => true,
    }
}
<commit_msg>Use ProcessAction in Windows handler for clippy<commit_after>use anyhow::{bail, Result};
use std::process::{Child, Command};

use crate::task_handler::ProcessAction;
use log::info;

pub fn compile_shell_command(command_string: &str) -> Command {
    // Chain two `powershell` commands, one that sets the output encoding to utf8 and then the user provided one.
    let mut command = Command::new("powershell");
    command.arg("-c").arg(format!(
        "[Console]::OutputEncoding = [Text.UTF8Encoding]::UTF8; {}",
        command_string
    ));

    command
}

/// Send a signal to a windows process.
pub fn send_signal_to_child(
    _child: &Child,
    action: &ProcessAction,
    _children: bool,
) -> Result<bool> {
    match action {
        ProcessAction::Pause => bail!("Pause is not yet supported on windows."),
        ProcessAction::Resume => bail!("Resume is not yet supported on windows."),
        ProcessAction::Kill => bail!("Kill is not yet supported on windows."),
    }
}

/// Kill a child process
pub fn kill_child(task_id: usize, child: &mut Child, _kill_children: bool) -> bool {
    match child.kill() {
        Err(_) => {
            info!("Task {} has already finished by itself", task_id);
            false
        }
        Ok(_) => true,
    }
}
<|endoftext|>


<commit_before>//! Fulfillment Outbound Shipment API - Version 2010-10-01
//!
//! [Documentation](https://docs.developer.amazonservices.com/en_US/fba_outbound/FBAOutbound_Overview.html)

use chrono::{DateTime, Utc};
use client::{Client, Method};
mod types;
pub use self::types::*;
use super::types::ToIso8601;
use result::MwsResult;

static PATH: &'static str = "/FulfillmentOutboundShipment/2010-10-01";
static VERSION: &'static str = "2010-10-01";

#[allow(non_snake_case)]
#[derive(Debug, Default, Serialize, FromXmlStream)]
pub struct ListAllFulfillmentOrdersResponse {
  pub FulfillmentOrders: Vec<FulfillmentOrder>,
  pub NextToken: Option<String>,
}

response_envelope_type!(
  ListAllFulfillmentOrdersEnvelope<ListAllFulfillmentOrdersResponse>,
  "ListAllFulfillmentOrdersResponse",
  "ListAllFulfillmentOrdersResult"
);

response_envelope_type!(
  ListAllFulfillmentOrdersByNextTokenEnvelope<ListAllFulfillmentOrdersResponse>,
  "ListAllFulfillmentOrdersByNextTokenResponse",
  "ListAllFulfillmentOrdersByNextTokenResult"
);

/// Returns a list of fulfillment orders fulfilled after (or at) a specified date.
///
/// [Documentation](http://docs.developer.amazonservices.com/en_US/orders-2013-09-01/Orders_ListOrders.html)
#[allow(non_snake_case)]
pub fn ListAllFulfillmentOrders(
  client: &Client,
  query_start_date_time: DateTime<Utc>,
) -> MwsResult<ListAllFulfillmentOrdersResponse> {
  client
    .request_xml(
      Method::Post,
      PATH,
      VERSION,
      "ListAllFulfillmentOrders",
      vec![(
        "QueryStartDateTime".to_string(),
        query_start_date_time.to_iso8601(),
      )],
    ).map(|e: ListAllFulfillmentOrdersEnvelope| e.into_inner())
    .map_err(|err| err.into())
}

/// Returns the next page of fulfillment orders using the NextToken parameter.
///
/// [Documentation](https://docs.developer.amazonservices.com/en_US/fba_outbound/FBAOutbound_ListAllFulfillmentOrdersByNextToken.html)
#[allow(non_snake_case)]
pub fn ListAllFulfillmentOrdersByNextToken(
  client: &Client,
  next_token: String,
) -> MwsResult<ListAllFulfillmentOrdersResponse> {
  let params = vec![("NextToken".to_string(), next_token)];
  client
    .request_xml(
      Method::Post,
      PATH,
      VERSION,
      "ListAllFulfillmentOrdersByNextToken",
      params,
    ).map(|e: ListAllFulfillmentOrdersByNextTokenEnvelope| e.into_inner())
    .map_err(|err| err.into())
}

response_envelope_type!(
  GetFulfillmentOrderEnvelope<GetFulfillmentOrderResponse>,
  "GetFulfillmentOrderResponse",
  "GetFulfillmentOrderResult"
);

#[derive(Debug, Default, Serialize, FromXmlStream)]
#[allow(non_snake_case)]
pub struct GetFulfillmentOrderResponse {
  pub FulfillmentShipment: Vec<FulfillmentShipment>,
  // pub ReturnItemList: Vec<ReturnItemList>,
  // pub ReturnAuthorizationList: Vec<ReturnAuthorizationList>,
  pub FulfillmentOrder: FulfillmentOrder,
  pub FulfillmentOrderItem: Vec<FulfillmentOrderItem>,
}

/// Returns a fulfillment order based on a specified SellerFulfillmentOrderId.
///
/// [Documentation](http://docs.developer.amazonservices.com/en_CA/fba_outbound/FBAOutbound_GetFulfillmentOrder.html)
#[allow(non_snake_case)]
pub fn GetFulfillmentOrder(
  client: &Client,
  seller_fulfillment_order_id: String,
) -> MwsResult<GetFulfillmentOrderResponse> {
  let params = vec![(
    "SellerFulfillmentOrderId".to_string(),
    seller_fulfillment_order_id,
  )];
  client
    .request_xml(Method::Post, PATH, VERSION, "GetFulfillmentOrder", params)
    .map(|e: GetFulfillmentOrderEnvelope| e.into_inner())
    .map_err(|err| err.into())
}

pub type GetPackageTrackingDetailsResponse = PackageTrackingDetails;

response_envelope_type!(
  GetPackageTrackingDetailsEnvelope<GetPackageTrackingDetailsResponse>,
  "GetPackageTrackingDetailsResponse",
  "GetPackageTrackingDetailsResult"
);

/// Returns delivery tracking information for a package in an outbound shipment for a Multi-Channel Fulfillment order.
///
/// [Documentation](http://docs.developer.amazonservices.com/en_US/fba_outbound/FBAOutbound_GetPackageTrackingDetails.html)
#[allow(non_snake_case)]
pub fn GetPackageTrackingDetails(
  client: &Client,
  package_number: &str,
) -> MwsResult<GetPackageTrackingDetailsResponse> {
  let params = vec![("PackageNumber".to_string(), package_number.to_owned())];
  client
    .request_xml(
      Method::Post,
      PATH,
      VERSION,
      "GetPackageTrackingDetails",
      params,
    ).map(|e: GetPackageTrackingDetailsEnvelope| e.into_inner())
    .map_err(|err| err.into())
}

/// Item information for a fulfillment order preview.
#[allow(non_snake_case)]
#[derive(Debug, Default, Serialize, SerializeMwsParams)]
pub struct GetFulfillmentPreviewItem {
  /// The seller SKU of the item.
  pub SellerSKU: String,
  /// A fulfillment order item identifier that you
  /// created with a call to the
  /// GetFulfillmentPreview operation.
  pub SellerFulfillmentOrderItemId: String,
  /// The item quantity.
  pub Quantity: i32,
}

/// Parameters for `GetFulfillmentPreview`
#[allow(non_snake_case)]
#[derive(Debug, Serialize, SerializeMwsParams)]
pub struct GetFulfillmentPreviewParameters {
  pub Address: DestinationAddress,
  pub Items: Vec<GetFulfillmentPreviewItem>,

  // Optional API Parameters
  pub MarketplaceId: Option<String>,
  pub ShippingSpeedCategories: Option<Vec<ShippingSpeedCategory>>,
}

#[derive(Debug, Default, Serialize, FromXmlStream)]
#[allow(non_snake_case)]
pub struct GetFulfillmentPreviewResponse {
  pub FulfillmentPreviews: Vec<FulfillmentPreview>,
}

response_envelope_type!(
  GetFulfillmentPreviewEnvelope<GetFulfillmentPreviewResponse>,
  "GetFulfillmentPreviewResponse",
  "GetFulfillmentPreviewResult"
);

/// Returns a list of fulfillment order previews based on shipping criteria that you specify.
///
/// [Documentation](https://docs.developer.amazonservices.com/en_US/fba_outbound/FBAOutbound_GetFulfillmentPreview.html)
#[allow(non_snake_case)]
pub fn GetFulfillmentPreview(
  client: &Client,
  params: GetFulfillmentPreviewParameters,
) -> MwsResult<GetFulfillmentPreviewResponse> {
  client
    .request_xml(Method::Post, PATH, VERSION, "GetFulfillmentPreview", params)
    .map(|e: GetFulfillmentPreviewEnvelope| e.into_inner())
    .map_err(|err| err.into())
}

/// Item information for creating a fulfillment order.
#[allow(non_snake_case)]
#[derive(Debug, Default, Serialize, SerializeMwsParams)]
pub struct CreateFulfillmentOrderItem {
  /// The seller SKU of the item.
  pub SellerSKU: Option<String>,
  /// Amazon's fulfillment network SKU of the item.
  pub FulfillmentNetworkSKU: Option<String>,
  /// A fulfillment order item identifier that you
  /// created with a call to the
  /// GetFulfillmentPreview operation.
  pub SellerFulfillmentOrderItemId: String,
  /// The item quantity.
  pub Quantity: i32,
  /// A message to the gift recipient, if applicable.
  pub GiftMessage: Option<String>,
  /// Item-specific text that displays in recipient-facing
  /// materials such as the outbound shipment packing slip.
  pub DisplayableComment: Option<String>,
  /// The monetary value assigned by the seller to this item.
  pub PerUnitDeclaredValue: Option<Currency>,
  /// The amount to be collected from the customer
  /// for this item in a COD (Cash On Delivery) order.
  pub PerUnitPrice: Option<Currency>,
  /// The tax on the amount to be collected from the customer
  /// for this item in a COD (Cash On Delivery) order.
  pub PerUnitTax: Option<Currency>,
}

/// Parameters for `CreateFulfillmentOrder`
#[allow(non_snake_case)]
#[derive(Debug, Serialize, SerializeMwsParams)]
pub struct CreateFulfillmentOrderParameters {
  pub SellerFulfillmentOrderId: String,
  pub ShippingSpeedCategory: ShippingSpeedCategory,
  pub DisplayableOrderId: String,
  pub DisplayableOrderDateTime: DateTime<Utc>,
  pub DisplayableOrderComment: String,
  pub DestinationAddress: DestinationAddress,
  pub Items: Vec<CreateFulfillmentOrderItem>,

  // Optional API Parameters
  pub MarketplaceId: Option<String>,
  pub ShipFromCountryCode: Option<String>,
  pub FulfillmentPolicy: Option<FulfillmentPolicy>,
  pub FulfillmentAction: Option<FulfillmentAction>,
  pub NotificationEmailList: Option<Vec<String>>,
}

response_envelope_type!(
  CreateFulfillmentOrderEnvelope<()>,
  "CreateFulfillmentOrderResponse",
  "CreateFulfillmentOrderResult"
);

/// Requests that Amazon ship items from the seller's inventory in Amazon's fulfillment network to a destination address.
///
/// [Documentation](https://docs.developer.amazonservices.com/en_US/fba_outbound/FBAOutbound_CreateFulfillmentOrder.html)
#[allow(non_snake_case)]
pub fn CreateFulfillmentOrder(
  client: &Client,
  params: CreateFulfillmentOrderParameters,
) -> MwsResult<()> {
  client
    .request_xml(
      Method::Post,
      PATH,
      VERSION,
      "CreateFulfillmentOrder",
      params,
    ).map(|e: CreateFulfillmentOrderEnvelope| e.into_inner())
    .map_err(|err| err.into())
}

response_envelope_type!(
  CancelFulfillmentOrderEnvelope<()>,
  "CancelFulfillmentOrderResponse",
  "CancelFulfillmentOrderResult"
);

/// Requests that Amazon stop attempting to fulfill an existing fulfillment order.
///
/// [Documentation](https://docs.developer.amazonservices.com/en_US/fba_outbound/FBAOutbound_CancelFulfillmentOrder.html)
#[allow(non_snake_case)]
pub fn CancelFulfillmentOrder(client: &Client, seller_fulfillment_order_id: &str) -> MwsResult<()> {
  client
    .request_xml(
      Method::Post,
      PATH,
      VERSION,
      "CancelFulfillmentOrder",
      vec![(
        "SellerFulfillmentOrderId".to_owned(),
        seller_fulfillment_order_id.to_owned(),
      )],
    ).map(|e: CancelFulfillmentOrderEnvelope| e.into_inner())
    .map_err(|err| err.into())
}

#[cfg(test)]
mod tests {
  use super::super::client::get_test_client;
  use super::*;
  use dotenv::dotenv;

  #[test]
  #[ignore]
  fn test_get_package_tracking_details() {
    dotenv().ok();
    let c = get_test_client();
    let res = GetPackageTrackingDetails(&c, "187748827").expect("GetPackageTrackingDetails");
    println!("res = {:#?}", res);
  }

  #[test]
  #[ignore]
  fn test_get_fufillment_preview() {
    dotenv().ok();
    let c = get_test_client();
    let res = GetFulfillmentPreview(
      &c,
      GetFulfillmentPreviewParameters {
        Address: DestinationAddress {
          PhoneNumber: "".to_owned(),
          City: "North York".to_owned(),
          CountryCode: "CA".to_owned(),
          PostalCode: "M2N0E9".to_owned(),
          Name: "Foo Bar".to_owned(),
          StateOrProvinceCode: "ON".to_owned(),
          DistrictOrCounty: "".to_owned(),
          Line1: "1000-7777 Yonge Street".to_owned(),
          Line2: "".to_owned(),
          Line3: "".to_owned(),
        },
        Items: vec![
          GetFulfillmentPreviewItem {
            SellerSKU: "e1".to_owned(),
            SellerFulfillmentOrderItemId: "1".to_owned(),
            Quantity: 1,
          },
          GetFulfillmentPreviewItem {
            SellerSKU: "e2".to_owned(),
            SellerFulfillmentOrderItemId: "2".to_owned(),
            Quantity: 139,
          },
        ],
        MarketplaceId: Some("A2EUQ1WTGCTBG2".to_string()),
        ShippingSpeedCategories: Some(vec![
          ShippingSpeedCategory::Standard,
          ShippingSpeedCategory::Expedited,
          ShippingSpeedCategory::Priority,
        ]),
      },
    ).expect("GetFulfillmentPreview");
    println!("res = {:#?}", res);
  }

  #[test]
  #[ignore]
  fn test_create_fulfillment_order() {
    use chrono::TimeZone;

    dotenv().ok();
    let c = get_test_client();
    let res = CreateFulfillmentOrder(
      &c,
      CreateFulfillmentOrderParameters {
        SellerFulfillmentOrderId: "S2_TEST_20180517_3".to_owned(),
        ShippingSpeedCategory: ShippingSpeedCategory::Expedited,
        DisplayableOrderId: "TEST".to_owned(),
        DisplayableOrderDateTime: Utc.ymd(2018, 5, 5).and_hms(0, 0, 0),
        DisplayableOrderComment: "DisplayableOrderComment".to_owned(),
        DestinationAddress: DestinationAddress {
          PhoneNumber: "".to_owned(),
          City: "North York".to_owned(),
          CountryCode: "CA".to_owned(),
          PostalCode: "M2N0E9".to_owned(),
          Name: "F A".to_owned(),
          StateOrProvinceCode: "ON".to_owned(),
          DistrictOrCounty: "".to_owned(),
          Line1: "88-888 Yonge Street".to_owned(),
          Line2: "".to_owned(),
          Line3: "".to_owned(),
        },
        Items: vec![CreateFulfillmentOrderItem {
          SellerSKU: "e-fba".to_owned(),
          SellerFulfillmentOrderItemId: "1".to_owned(),
          Quantity: 1,
          GiftMessage: Some("GiftMessage".to_owned()),
          DisplayableComment: Some("DisplayableComment".to_owned()),
          FulfillmentNetworkSKU: None,
          PerUnitDeclaredValue: None,
          PerUnitPrice: None,
          PerUnitTax: None,
        }],
        MarketplaceId: Some("A2EUQ1WTGCTBG2".to_string()),
        ShipFromCountryCode: None,
        FulfillmentPolicy: Some(FulfillmentPolicy::FillOrKill),
        FulfillmentAction: Some(FulfillmentAction::Hold),
        NotificationEmailList: Some(vec!["a@gmail.com".to_owned(), "b@ventmere.com".to_owned()]),
      },
    ).expect("CreateFulfillmentOrder");
    println!("res = {:#?}", res);
  }

  #[test]
  #[ignore]
  fn test_cancel_fulfillment_order() {
    dotenv().ok();
    let c = get_test_client();
    let res = CancelFulfillmentOrder(&c, "S2_TEST_20180517_3").expect("CancelFulfillmentOrder");
    println!("res = {:#?}", res);
  }
}
<commit_msg>SellerSKU is required.<commit_after>//! Fulfillment Outbound Shipment API - Version 2010-10-01
//!
//! [Documentation](https://docs.developer.amazonservices.com/en_US/fba_outbound/FBAOutbound_Overview.html)

use chrono::{DateTime, Utc};
use client::{Client, Method};
mod types;
pub use self::types::*;
use super::types::ToIso8601;
use result::MwsResult;

static PATH: &'static str = "/FulfillmentOutboundShipment/2010-10-01";
static VERSION: &'static str = "2010-10-01";

#[allow(non_snake_case)]
#[derive(Debug, Default, Serialize, FromXmlStream)]
pub struct ListAllFulfillmentOrdersResponse {
  pub FulfillmentOrders: Vec<FulfillmentOrder>,
  pub NextToken: Option<String>,
}

response_envelope_type!(
  ListAllFulfillmentOrdersEnvelope<ListAllFulfillmentOrdersResponse>,
  "ListAllFulfillmentOrdersResponse",
  "ListAllFulfillmentOrdersResult"
);

response_envelope_type!(
  ListAllFulfillmentOrdersByNextTokenEnvelope<ListAllFulfillmentOrdersResponse>,
  "ListAllFulfillmentOrdersByNextTokenResponse",
  "ListAllFulfillmentOrdersByNextTokenResult"
);

/// Returns a list of fulfillment orders fulfilled after (or at) a specified date.
///
/// [Documentation](http://docs.developer.amazonservices.com/en_US/orders-2013-09-01/Orders_ListOrders.html)
#[allow(non_snake_case)]
pub fn ListAllFulfillmentOrders(
  client: &Client,
  query_start_date_time: DateTime<Utc>,
) -> MwsResult<ListAllFulfillmentOrdersResponse> {
  client
    .request_xml(
      Method::Post,
      PATH,
      VERSION,
      "ListAllFulfillmentOrders",
      vec![(
        "QueryStartDateTime".to_string(),
        query_start_date_time.to_iso8601(),
      )],
    ).map(|e: ListAllFulfillmentOrdersEnvelope| e.into_inner())
    .map_err(|err| err.into())
}

/// Returns the next page of fulfillment orders using the NextToken parameter.
///
/// [Documentation](https://docs.developer.amazonservices.com/en_US/fba_outbound/FBAOutbound_ListAllFulfillmentOrdersByNextToken.html)
#[allow(non_snake_case)]
pub fn ListAllFulfillmentOrdersByNextToken(
  client: &Client,
  next_token: String,
) -> MwsResult<ListAllFulfillmentOrdersResponse> {
  let params = vec![("NextToken".to_string(), next_token)];
  client
    .request_xml(
      Method::Post,
      PATH,
      VERSION,
      "ListAllFulfillmentOrdersByNextToken",
      params,
    ).map(|e: ListAllFulfillmentOrdersByNextTokenEnvelope| e.into_inner())
    .map_err(|err| err.into())
}

response_envelope_type!(
  GetFulfillmentOrderEnvelope<GetFulfillmentOrderResponse>,
  "GetFulfillmentOrderResponse",
  "GetFulfillmentOrderResult"
);

#[derive(Debug, Default, Serialize, FromXmlStream)]
#[allow(non_snake_case)]
pub struct GetFulfillmentOrderResponse {
  pub FulfillmentShipment: Vec<FulfillmentShipment>,
  // pub ReturnItemList: Vec<ReturnItemList>,
  // pub ReturnAuthorizationList: Vec<ReturnAuthorizationList>,
  pub FulfillmentOrder: FulfillmentOrder,
  pub FulfillmentOrderItem: Vec<FulfillmentOrderItem>,
}

/// Returns a fulfillment order based on a specified SellerFulfillmentOrderId.
///
/// [Documentation](http://docs.developer.amazonservices.com/en_CA/fba_outbound/FBAOutbound_GetFulfillmentOrder.html)
#[allow(non_snake_case)]
pub fn GetFulfillmentOrder(
  client: &Client,
  seller_fulfillment_order_id: String,
) -> MwsResult<GetFulfillmentOrderResponse> {
  let params = vec![(
    "SellerFulfillmentOrderId".to_string(),
    seller_fulfillment_order_id,
  )];
  client
    .request_xml(Method::Post, PATH, VERSION, "GetFulfillmentOrder", params)
    .map(|e: GetFulfillmentOrderEnvelope| e.into_inner())
    .map_err(|err| err.into())
}

pub type GetPackageTrackingDetailsResponse = PackageTrackingDetails;

response_envelope_type!(
  GetPackageTrackingDetailsEnvelope<GetPackageTrackingDetailsResponse>,
  "GetPackageTrackingDetailsResponse",
  "GetPackageTrackingDetailsResult"
);

/// Returns delivery tracking information for a package in an outbound shipment for a Multi-Channel Fulfillment order.
///
/// [Documentation](http://docs.developer.amazonservices.com/en_US/fba_outbound/FBAOutbound_GetPackageTrackingDetails.html)
#[allow(non_snake_case)]
pub fn GetPackageTrackingDetails(
  client: &Client,
  package_number: &str,
) -> MwsResult<GetPackageTrackingDetailsResponse> {
  let params = vec![("PackageNumber".to_string(), package_number.to_owned())];
  client
    .request_xml(
      Method::Post,
      PATH,
      VERSION,
      "GetPackageTrackingDetails",
      params,
    ).map(|e: GetPackageTrackingDetailsEnvelope| e.into_inner())
    .map_err(|err| err.into())
}

/// Item information for a fulfillment order preview.
#[allow(non_snake_case)]
#[derive(Debug, Default, Serialize, SerializeMwsParams)]
pub struct GetFulfillmentPreviewItem {
  /// The seller SKU of the item.
  pub SellerSKU: String,
  /// A fulfillment order item identifier that you
  /// created with a call to the
  /// GetFulfillmentPreview operation.
  pub SellerFulfillmentOrderItemId: String,
  /// The item quantity.
  pub Quantity: i32,
}

/// Parameters for `GetFulfillmentPreview`
#[allow(non_snake_case)]
#[derive(Debug, Serialize, SerializeMwsParams)]
pub struct GetFulfillmentPreviewParameters {
  pub Address: DestinationAddress,
  pub Items: Vec<GetFulfillmentPreviewItem>,

  // Optional API Parameters
  pub MarketplaceId: Option<String>,
  pub ShippingSpeedCategories: Option<Vec<ShippingSpeedCategory>>,
}

#[derive(Debug, Default, Serialize, FromXmlStream)]
#[allow(non_snake_case)]
pub struct GetFulfillmentPreviewResponse {
  pub FulfillmentPreviews: Vec<FulfillmentPreview>,
}

response_envelope_type!(
  GetFulfillmentPreviewEnvelope<GetFulfillmentPreviewResponse>,
  "GetFulfillmentPreviewResponse",
  "GetFulfillmentPreviewResult"
);

/// Returns a list of fulfillment order previews based on shipping criteria that you specify.
///
/// [Documentation](https://docs.developer.amazonservices.com/en_US/fba_outbound/FBAOutbound_GetFulfillmentPreview.html)
#[allow(non_snake_case)]
pub fn GetFulfillmentPreview(
  client: &Client,
  params: GetFulfillmentPreviewParameters,
) -> MwsResult<GetFulfillmentPreviewResponse> {
  client
    .request_xml(Method::Post, PATH, VERSION, "GetFulfillmentPreview", params)
    .map(|e: GetFulfillmentPreviewEnvelope| e.into_inner())
    .map_err(|err| err.into())
}

/// Item information for creating a fulfillment order.
#[allow(non_snake_case)]
#[derive(Debug, Default, Serialize, SerializeMwsParams)]
pub struct CreateFulfillmentOrderItem {
  /// The seller SKU of the item.
  pub SellerSKU: String,
  /// Amazon's fulfillment network SKU of the item.
  pub FulfillmentNetworkSKU: Option<String>,
  /// A fulfillment order item identifier that you
  /// created with a call to the
  /// GetFulfillmentPreview operation.
  pub SellerFulfillmentOrderItemId: String,
  /// The item quantity.
  pub Quantity: i32,
  /// A message to the gift recipient, if applicable.
  pub GiftMessage: Option<String>,
  /// Item-specific text that displays in recipient-facing
  /// materials such as the outbound shipment packing slip.
  pub DisplayableComment: Option<String>,
  /// The monetary value assigned by the seller to this item.
  pub PerUnitDeclaredValue: Option<Currency>,
  /// The amount to be collected from the customer
  /// for this item in a COD (Cash On Delivery) order.
  pub PerUnitPrice: Option<Currency>,
  /// The tax on the amount to be collected from the customer
  /// for this item in a COD (Cash On Delivery) order.
  pub PerUnitTax: Option<Currency>,
}

/// Parameters for `CreateFulfillmentOrder`
#[allow(non_snake_case)]
#[derive(Debug, Serialize, SerializeMwsParams)]
pub struct CreateFulfillmentOrderParameters {
  pub SellerFulfillmentOrderId: String,
  pub ShippingSpeedCategory: ShippingSpeedCategory,
  pub DisplayableOrderId: String,
  pub DisplayableOrderDateTime: DateTime<Utc>,
  pub DisplayableOrderComment: String,
  pub DestinationAddress: DestinationAddress,
  pub Items: Vec<CreateFulfillmentOrderItem>,

  // Optional API Parameters
  pub MarketplaceId: Option<String>,
  pub ShipFromCountryCode: Option<String>,
  pub FulfillmentPolicy: Option<FulfillmentPolicy>,
  pub FulfillmentAction: Option<FulfillmentAction>,
  pub NotificationEmailList: Option<Vec<String>>,
}

response_envelope_type!(
  CreateFulfillmentOrderEnvelope<()>,
  "CreateFulfillmentOrderResponse",
  "CreateFulfillmentOrderResult"
);

/// Requests that Amazon ship items from the seller's inventory in Amazon's fulfillment network to a destination address.
///
/// [Documentation](https://docs.developer.amazonservices.com/en_US/fba_outbound/FBAOutbound_CreateFulfillmentOrder.html)
#[allow(non_snake_case)]
pub fn CreateFulfillmentOrder(
  client: &Client,
  params: CreateFulfillmentOrderParameters,
) -> MwsResult<()> {
  client
    .request_xml(
      Method::Post,
      PATH,
      VERSION,
      "CreateFulfillmentOrder",
      params,
    ).map(|e: CreateFulfillmentOrderEnvelope| e.into_inner())
    .map_err(|err| err.into())
}

response_envelope_type!(
  CancelFulfillmentOrderEnvelope<()>,
  "CancelFulfillmentOrderResponse",
  "CancelFulfillmentOrderResult"
);

/// Requests that Amazon stop attempting to fulfill an existing fulfillment order.
///
/// [Documentation](https://docs.developer.amazonservices.com/en_US/fba_outbound/FBAOutbound_CancelFulfillmentOrder.html)
#[allow(non_snake_case)]
pub fn CancelFulfillmentOrder(client: &Client, seller_fulfillment_order_id: &str) -> MwsResult<()> {
  client
    .request_xml(
      Method::Post,
      PATH,
      VERSION,
      "CancelFulfillmentOrder",
      vec![(
        "SellerFulfillmentOrderId".to_owned(),
        seller_fulfillment_order_id.to_owned(),
      )],
    ).map(|e: CancelFulfillmentOrderEnvelope| e.into_inner())
    .map_err(|err| err.into())
}

#[cfg(test)]
mod tests {
  use super::super::client::get_test_client;
  use super::*;
  use dotenv::dotenv;

  #[test]
  #[ignore]
  fn test_get_package_tracking_details() {
    dotenv().ok();
    let c = get_test_client();
    let res = GetPackageTrackingDetails(&c, "187748827").expect("GetPackageTrackingDetails");
    println!("res = {:#?}", res);
  }

  #[test]
  #[ignore]
  fn test_get_fufillment_preview() {
    dotenv().ok();
    let c = get_test_client();
    let res = GetFulfillmentPreview(
      &c,
      GetFulfillmentPreviewParameters {
        Address: DestinationAddress {
          PhoneNumber: "".to_owned(),
          City: "North York".to_owned(),
          CountryCode: "CA".to_owned(),
          PostalCode: "M2N0E9".to_owned(),
          Name: "Foo Bar".to_owned(),
          StateOrProvinceCode: "ON".to_owned(),
          DistrictOrCounty: "".to_owned(),
          Line1: "1000-7777 Yonge Street".to_owned(),
          Line2: "".to_owned(),
          Line3: "".to_owned(),
        },
        Items: vec![
          GetFulfillmentPreviewItem {
            SellerSKU: "e1".to_owned(),
            SellerFulfillmentOrderItemId: "1".to_owned(),
            Quantity: 1,
          },
          GetFulfillmentPreviewItem {
            SellerSKU: "e2".to_owned(),
            SellerFulfillmentOrderItemId: "2".to_owned(),
            Quantity: 139,
          },
        ],
        MarketplaceId: Some("A2EUQ1WTGCTBG2".to_string()),
        ShippingSpeedCategories: Some(vec![
          ShippingSpeedCategory::Standard,
          ShippingSpeedCategory::Expedited,
          ShippingSpeedCategory::Priority,
        ]),
      },
    ).expect("GetFulfillmentPreview");
    println!("res = {:#?}", res);
  }

  #[test]
  #[ignore]
  fn test_create_fulfillment_order() {
    use chrono::TimeZone;

    dotenv().ok();
    let c = get_test_client();
    let res = CreateFulfillmentOrder(
      &c,
      CreateFulfillmentOrderParameters {
        SellerFulfillmentOrderId: "S2_TEST_20180517_3".to_owned(),
        ShippingSpeedCategory: ShippingSpeedCategory::Expedited,
        DisplayableOrderId: "TEST".to_owned(),
        DisplayableOrderDateTime: Utc.ymd(2018, 5, 5).and_hms(0, 0, 0),
        DisplayableOrderComment: "DisplayableOrderComment".to_owned(),
        DestinationAddress: DestinationAddress {
          PhoneNumber: "".to_owned(),
          City: "North York".to_owned(),
          CountryCode: "CA".to_owned(),
          PostalCode: "M2N0E9".to_owned(),
          Name: "F A".to_owned(),
          StateOrProvinceCode: "ON".to_owned(),
          DistrictOrCounty: "".to_owned(),
          Line1: "88-888 Yonge Street".to_owned(),
          Line2: "".to_owned(),
          Line3: "".to_owned(),
        },
        Items: vec![CreateFulfillmentOrderItem {
          SellerSKU: "e-fba".to_owned(),
          SellerFulfillmentOrderItemId: "1".to_owned(),
          Quantity: 1,
          GiftMessage: Some("GiftMessage".to_owned()),
          DisplayableComment: Some("DisplayableComment".to_owned()),
          FulfillmentNetworkSKU: None,
          PerUnitDeclaredValue: None,
          PerUnitPrice: None,
          PerUnitTax: None,
        }],
        MarketplaceId: Some("A2EUQ1WTGCTBG2".to_string()),
        ShipFromCountryCode: None,
        FulfillmentPolicy: Some(FulfillmentPolicy::FillOrKill),
        FulfillmentAction: Some(FulfillmentAction::Hold),
        NotificationEmailList: Some(vec!["a@gmail.com".to_owned(), "b@ventmere.com".to_owned()]),
      },
    ).expect("CreateFulfillmentOrder");
    println!("res = {:#?}", res);
  }

  #[test]
  #[ignore]
  fn test_cancel_fulfillment_order() {
    dotenv().ok();
    let c = get_test_client();
    let res = CancelFulfillmentOrder(&c, "S2_TEST_20180517_3").expect("CancelFulfillmentOrder");
    println!("res = {:#?}", res);
  }
}
<|endoftext|>