<commit_before>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

//! The Aptos API response / error handling philosophy.
//!
//! The return type for every endpoint should be a
//! poem::Result<MyResponse<T>, MyError> where MyResponse is an instance of
//! ApiResponse that contains only the status codes that it can actually
//! return. This will manifest in the OpenAPI spec, making it clear to users
//! what the API can actually return. The error should operate the same way,
//! where it only describes error codes that it can actually return.
//!
//! Every endpoint should be able to return data as JSON or BCS, depending on
//! the Accept header given by the client. If none is given, default to JSON.
//!
//! The client should be able to provide data to POST endpoints as either JSON
//! or BCS, given they provide the appropriate Content-Type header.
//!
//! Where possible, if the API is returning data as BCS, it should pull the
//! bytes directly from the DB where possible without further processing.
//!
//! Internally, there are many functions that can return errors. The context
//! for what type of error those functions return is lost if we return an
//! opaque error type like anyhow::Error. As such, it is important that each
//! function return error types that capture the intended status code. This
//! module defines traits to help with this, ensuring that the error types
//! returned by each function and its callers is enforced at compile time.
//! See generate_error_traits and its invocations for more on this topic.

// TODO: https://github.com/aptos-labs/aptos-core/issues/2279

use std::fmt::Display;

use super::accept_type::AcceptType;
use aptos_api_types::{AptosError, AptosErrorCode};
use poem_openapi::{payload::Json, types::ToJSON, ResponseContent};

use super::bcs_payload::Bcs;

#[derive(ResponseContent)]
pub enum AptosResponseContent<T: ToJSON + Send + Sync> {
    // When returning data as JSON, we take in T and then serialize to JSON as
    // part of the response.
    Json(Json<T>),

    // Return the data as BCS, which is just Vec<u8>. This data could have come
    // from either an internal Rust type being serialized into bytes, or just
    // the bytes directly from storage.
    Bcs(Bcs),
}

/// This trait defines common functions that all error responses should impl.
/// As a user you shouldn't worry about this, the generate_error_response macro
/// takes care of it for you. Mostly these are helpers to allow callers holding
/// an error response to manipulate the AptosError inside it.
pub trait AptosErrorResponse {
    fn inner_mut(&mut self) -> &mut AptosError;

    fn error_code(mut self, error_code: AptosErrorCode) -> Self
    where
        Self: Sized,
    {
        self.inner_mut().error_code = Some(error_code);
        self
    }

    fn aptos_ledger_version(mut self, aptos_ledger_version: u64) -> Self
    where
        Self: Sized,
    {
        self.inner_mut().aptos_ledger_version = Some(aptos_ledger_version.into());
        self
    }
}

/// This macro defines traits for all of the given status codes. In eahc trait
/// there is a function that defines a helper for building an instance of the
/// error type using that code. These traits are helpful for defining what
/// error types an internal function can return. For example, the failpoint
/// function can return an internal error, so its signature would look like:
/// fn fail_point_poem<E: InternalError>(name: &str) -> anyhow::Result<(), E>
/// This should be invoked just once, taking in every status that we use
/// throughout the entire API. Every one of these traits requires that the
/// implementor also implements AptosErrorResponse, which saves functions from
/// having to add that bound to errors themselves.
#[macro_export]
macro_rules! generate_error_traits {
    ($($trait_name:ident),*) => {
        paste::paste! {
        $(
        pub trait [<$trait_name Error>]: AptosErrorResponse {
            fn [<$trait_name:snake>](error: anyhow::Error) -> Self where Self: Sized;
            fn [<$trait_name:snake _str>](error_str: &str) -> Self where Self: Sized;
        }
        )*
        }
    };
}

/// This macro helps generate types, From impls, etc. for an error
/// response from a Poem endpoint. It generates a response type that only has
/// the specified response codes, which is then reflected in the OpenAPI spec.
/// For each status code given to a particular invocation of this macro, we
/// implement the relevant trait from generate_error_traits.
/// See the comments in the macro for an explanation of what is happening.
#[macro_export]
macro_rules! generate_error_response {
    ($enum_name:ident, $(($status:literal, $name:ident)),*) => {
        // We use the paste crate to allows us to generate the name of the code
        // enum, more on that in the comment above that enum.
        paste::paste! {

        // Generate an enum with name `enum_name`. Iterate through each of the
        // response codes, generating a variant for each with the given name
        // and status code. We always generate a variant for 500.
        #[derive(Debug, poem_openapi::ApiResponse)]
        pub enum $enum_name {
            $(
            #[oai(status = $status)]
            $name(poem_openapi::payload::Json<aptos_api_types::AptosError>),
            )*
        }

        // For each status, implement the relevant error trait. This means if
        // the macro invocation specifies Internal and BadRequest, the
        // functions internal(anyhow::Error) and bad_request(anyhow::Error)
        // will be generated. There are also variants for taking in strs.
        $(
        impl $crate::response::[<$name Error>] for $enum_name {
            fn [<$name:snake>](error: anyhow::Error) -> Self where Self: Sized {
                let error = aptos_api_types::AptosError::from(error);
                let payload = poem_openapi::payload::Json(error);
                Self::from($enum_name::$name(payload))
            }

            fn [<$name:snake _str>](error_str: &str) -> Self where Self: Sized {
                let error = aptos_api_types::AptosError::new(error_str.to_string());
                let payload = poem_openapi::payload::Json(error);
                Self::from($enum_name::$name(payload))
            }
        }
        )*
        }

        // Generate a function that helps get the AptosError within.
        impl $crate::response::AptosErrorResponse for $enum_name {
            fn inner_mut(&mut self) -> &mut aptos_api_types::AptosError {
                match self {
                    $(
                    $enum_name::$name(poem_openapi::payload::Json(inner)) => inner,
                    )*
                }
            }
        }

        impl std::error::Error for $enum_name {}

        impl std::fmt::Display for $enum_name {
            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
                write!(f, "{:?}", self)
            }
        }
    };
}

/// This macro helps generate types, From impls, etc. for a successful response
/// from a Poem endpoint. It generates a response type that only has the
/// specified response codes, which is then reflected in the OpenAPI spec.
/// See the comments in the macro for an explanation of what is happening.
#[macro_export]
macro_rules! generate_success_response {
    ($enum_name:ident, $(($status:literal, $name:ident)),*) => {
        // We use the paste crate to allows us to generate the name of the code
        // enum, more on that in the comment above that enum.
        paste::paste! {

        // Generate an enum with name `enum_name`. Iterate through each of the
        // response codes, generating a variant for each with the given name
        // and status code.
        #[derive(poem_openapi::ApiResponse)]
        pub enum $enum_name<T: poem_openapi::types::ToJSON + Send + Sync> {
            $(
            #[oai(status = $status)]
            $name(
                $crate::response::AptosResponseContent<T>,
                #[oai(header = "X-Aptos-Chain-Id")] u16,
                // We use just regular u64 here instead of U64 since all header
                // values are implicitly strings anyway.
                #[oai(header = "X-Aptos-Ledger-Version")] u64,
                #[oai(header = "X-Aptos-Ledger-Oldest-Version")] u64,
                #[oai(header = "X-Aptos-Ledger-TimestampUsec")] u64,
                #[oai(header = "X-Aptos-Epoch")] u64,
                #[oai(header = "X-Aptos-Block-Height")] u64,
                #[oai(header = "X-Aptos-Oldest-Block-Height")] u64,
            ),
            )*
        }

        // Generate an enum that captures all the different status codes that
        // this response type supports. To explain this funky syntax, if you
        // named the main enum MyResponse, this would become MyResponseCode.
        pub enum [<$enum_name Status>] {
            $(
            $name,
            )*
        }

        // Generate a From impl that builds a response from AptosResponseContent.
        // Each variant in the main enum takes in the same argument, so the macro
        // is really just helping us enumerate and build each variant. We use this
        // in the other From impls.
        impl <T: poem_openapi::types::ToJSON + Send + Sync> From<($crate::response::AptosResponseContent<T>, &aptos_api_types::LedgerInfo, [<$enum_name Status>])>
            for $enum_name<T>
        {
            fn from(
                (value, ledger_info, status): (
                    $crate::response::AptosResponseContent<T>,
                    &aptos_api_types::LedgerInfo,
                    [<$enum_name Status>]
                ),
            ) -> Self {
                match status {
                    $(
                    [<$enum_name Status>]::$name => {
                        $enum_name::$name(
                            value,
                            ledger_info.chain_id as u16,
                            ledger_info.ledger_version.into(),
                            ledger_info.oldest_ledger_version.into(),
                            ledger_info.ledger_timestamp.into(),
                            ledger_info.epoch.into(),
                            ledger_info.block_height.into(),
                            ledger_info.oldest_block_height.into(),
                        )
                    },
                    )*
                }
            }
        }

        // Generate a From impl that builds a response from a Json<T> and friends.
        impl<T: poem_openapi::types::ToJSON + Send + Sync> From<(poem_openapi::payload::Json<T>, &aptos_api_types::LedgerInfo, [<$enum_name Status>])>
            for $enum_name<T>
        {
            fn from(
                (value, ledger_info, status): (poem_openapi::payload::Json<T>, &aptos_api_types::LedgerInfo, [<$enum_name Status>]),
            ) -> Self {
                let content = $crate::response::AptosResponseContent::Json(value);
                Self::from((content, ledger_info, status))
            }
        }

        // Generate a From impl that builds a response from a Bcs<Vec<u8>> and friends.
        impl<T: poem_openapi::types::ToJSON + Send + Sync> From<($crate::bcs_payload::Bcs, &aptos_api_types::LedgerInfo, [<$enum_name Status>])>
            for $enum_name<T>
        {
            fn from(
                (value, ledger_info, status): (
                    $crate::bcs_payload::Bcs,
                    &aptos_api_types::LedgerInfo,
                    [<$enum_name Status>]
                ),
            ) -> Self {
                let content = $crate::response::AptosResponseContent::Bcs(value);
                Self::from((content, ledger_info, status))
            }
        }

        // Generate a TryFrom impl that builds a response from a T, an AcceptType,
        // and all the other usual suspects. It expects to be called with a generic
        // parameter E: InternalError, with which we can build an internal error
        // response in case the BCS serialization fails.
        impl<T: poem_openapi::types::ToJSON + Send + Sync + serde::Serialize> $enum_name<T> {
            pub fn try_from_rust_value<E: InternalError>(
                (value, ledger_info, status, accept_type): (
                    T,
                    &aptos_api_types::LedgerInfo,
                    [<$enum_name Status>],
                    &$crate::accept_type::AcceptType
                ),
            ) -> Result<Self, E> {
                match accept_type {
                    AcceptType::Bcs => Ok(Self::from((
                        $crate::bcs_payload::Bcs(
                            bcs::to_bytes(&value)
                                .map_err(|e| E::internal(e.into()).error_code(aptos_api_types::AptosErrorCode::BcsSerializationError))?
                        ),
                        ledger_info,
                        status
                    ))),
                    AcceptType::Json => Ok(Self::from((
                        poem_openapi::payload::Json(value),
                        ledger_info,
                        status
                    ))),
                }
            }

           pub fn try_from_json<E: InternalError>(
                (value, ledger_info, status): (
                    T,
                    &aptos_api_types::LedgerInfo,
                    [<$enum_name Status>],
                ),
            ) -> Result<Self, E> {
               Ok(Self::from((
                    poem_openapi::payload::Json(value),
                    ledger_info,
                    status
               )))
            }

            pub fn try_from_bcs<B: serde::Serialize, E: InternalError>(
                (value, ledger_info, status): (
                    B,
                    &aptos_api_types::LedgerInfo,
                    [<$enum_name Status>],
                ),
            ) -> Result<Self, E> {
               Ok(Self::from((
                    $crate::bcs_payload::Bcs(
                        bcs::to_bytes(&value)
                            .map_err(|e| E::internal(e.into()).error_code(aptos_api_types::AptosErrorCode::BcsSerializationError))?
                    ),
                    ledger_info,
                    status
               )))
            }

            pub fn try_from_encoded<E: InternalError>(
                (value, ledger_info, status): (
                    Vec<u8>,
                    &aptos_api_types::LedgerInfo,
                    [<$enum_name Status>],
                ),
            ) -> Result<Self, E> {
               Ok(Self::from((
                    $crate::bcs_payload::Bcs(
                        value
                    ),
                    ledger_info,
                    status
               )))
            }
        }
        }
    };
}

// Generate a success response that only has an option for 200.
generate_success_response!(BasicResponse, (200, Ok));

// Generate traits defining a "from" function for each of these status types.
// The error response then impls these traits for each status type they mention.
generate_error_traits!(
    BadRequest,
    NotFound,
    PayloadTooLarge,
    Internal,
    InsufficientStorage
);

// Generate an error response that only has options for 400 and 500.
generate_error_response!(BasicError, (400, BadRequest), (500, Internal));

// This type just simplifies using BasicResponse and BasicError together.
pub type BasicResult<T> = poem::Result<BasicResponse<T>, BasicError>;

// As above but with 404.
generate_error_response!(
    BasicErrorWith404,
    (400, BadRequest),
    (404, NotFound),
    (500, Internal)
);
pub type BasicResultWith404<T> = poem::Result<BasicResponse<T>, BasicErrorWith404>;

// Just this one helper for a specific kind of 404.
pub fn build_not_found<S: Display, E: NotFoundError>(
    resource: &str,
    identifier: S,
    ledger_version: u64,
) -> E {
    E::not_found_str(&format!("{} not found by {}", resource, identifier))
        .aptos_ledger_version(ledger_version)
}
<commit_msg>[api] Add service unavailable code<commit_after>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

//! The Aptos API response / error handling philosophy.
//!
//! The return type for every endpoint should be a
//! poem::Result<MyResponse<T>, MyError> where MyResponse is an instance of
//! ApiResponse that contains only the status codes that it can actually
//! return. This will manifest in the OpenAPI spec, making it clear to users
//! what the API can actually return. The error should operate the same way,
//! where it only describes error codes that it can actually return.
//!
//! Every endpoint should be able to return data as JSON or BCS, depending on
//! the Accept header given by the client. If none is given, default to JSON.
//!
//! The client should be able to provide data to POST endpoints as either JSON
//! or BCS, given they provide the appropriate Content-Type header.
//!
//! Where possible, if the API is returning data as BCS, it should pull the
//! bytes directly from the DB where possible without further processing.
//!
//! Internally, there are many functions that can return errors. The context
//! for what type of error those functions return is lost if we return an
//! opaque error type like anyhow::Error. As such, it is important that each
//! function return error types that capture the intended status code. This
//! module defines traits to help with this, ensuring that the error types
//! returned by each function and its callers is enforced at compile time.
//! See generate_error_traits and its invocations for more on this topic.

// TODO: https://github.com/aptos-labs/aptos-core/issues/2279

use std::fmt::Display;

use super::accept_type::AcceptType;
use aptos_api_types::{AptosError, AptosErrorCode};
use poem_openapi::{payload::Json, types::ToJSON, ResponseContent};

use super::bcs_payload::Bcs;

#[derive(ResponseContent)]
pub enum AptosResponseContent<T: ToJSON + Send + Sync> {
    // When returning data as JSON, we take in T and then serialize to JSON as
    // part of the response.
    Json(Json<T>),

    // Return the data as BCS, which is just Vec<u8>. This data could have come
    // from either an internal Rust type being serialized into bytes, or just
    // the bytes directly from storage.
    Bcs(Bcs),
}

/// This trait defines common functions that all error responses should impl.
/// As a user you shouldn't worry about this, the generate_error_response macro
/// takes care of it for you. Mostly these are helpers to allow callers holding
/// an error response to manipulate the AptosError inside it.
pub trait AptosErrorResponse {
    fn inner_mut(&mut self) -> &mut AptosError;

    fn error_code(mut self, error_code: AptosErrorCode) -> Self
    where
        Self: Sized,
    {
        self.inner_mut().error_code = Some(error_code);
        self
    }

    fn aptos_ledger_version(mut self, aptos_ledger_version: u64) -> Self
    where
        Self: Sized,
    {
        self.inner_mut().aptos_ledger_version = Some(aptos_ledger_version.into());
        self
    }
}

/// This macro defines traits for all of the given status codes. In eahc trait
/// there is a function that defines a helper for building an instance of the
/// error type using that code. These traits are helpful for defining what
/// error types an internal function can return. For example, the failpoint
/// function can return an internal error, so its signature would look like:
/// fn fail_point_poem<E: InternalError>(name: &str) -> anyhow::Result<(), E>
/// This should be invoked just once, taking in every status that we use
/// throughout the entire API. Every one of these traits requires that the
/// implementor also implements AptosErrorResponse, which saves functions from
/// having to add that bound to errors themselves.
#[macro_export]
macro_rules! generate_error_traits {
    ($($trait_name:ident),*) => {
        paste::paste! {
        $(
        pub trait [<$trait_name Error>]: AptosErrorResponse {
            fn [<$trait_name:snake>](error: anyhow::Error) -> Self where Self: Sized;
            fn [<$trait_name:snake _str>](error_str: &str) -> Self where Self: Sized;
        }
        )*
        }
    };
}

/// This macro helps generate types, From impls, etc. for an error
/// response from a Poem endpoint. It generates a response type that only has
/// the specified response codes, which is then reflected in the OpenAPI spec.
/// For each status code given to a particular invocation of this macro, we
/// implement the relevant trait from generate_error_traits.
/// See the comments in the macro for an explanation of what is happening.
#[macro_export]
macro_rules! generate_error_response {
    ($enum_name:ident, $(($status:literal, $name:ident)),*) => {
        // We use the paste crate to allows us to generate the name of the code
        // enum, more on that in the comment above that enum.
        paste::paste! {

        // Generate an enum with name `enum_name`. Iterate through each of the
        // response codes, generating a variant for each with the given name
        // and status code. We always generate a variant for 500.
        #[derive(Debug, poem_openapi::ApiResponse)]
        pub enum $enum_name {
            $(
            #[oai(status = $status)]
            $name(poem_openapi::payload::Json<aptos_api_types::AptosError>),
            )*
        }

        // For each status, implement the relevant error trait. This means if
        // the macro invocation specifies Internal and BadRequest, the
        // functions internal(anyhow::Error) and bad_request(anyhow::Error)
        // will be generated. There are also variants for taking in strs.
        $(
        impl $crate::response::[<$name Error>] for $enum_name {
            fn [<$name:snake>](error: anyhow::Error) -> Self where Self: Sized {
                let error = aptos_api_types::AptosError::from(error);
                let payload = poem_openapi::payload::Json(error);
                Self::from($enum_name::$name(payload))
            }

            fn [<$name:snake _str>](error_str: &str) -> Self where Self: Sized {
                let error = aptos_api_types::AptosError::new(error_str.to_string());
                let payload = poem_openapi::payload::Json(error);
                Self::from($enum_name::$name(payload))
            }
        }
        )*
        }

        // Generate a function that helps get the AptosError within.
        impl $crate::response::AptosErrorResponse for $enum_name {
            fn inner_mut(&mut self) -> &mut aptos_api_types::AptosError {
                match self {
                    $(
                    $enum_name::$name(poem_openapi::payload::Json(inner)) => inner,
                    )*
                }
            }
        }

        impl std::error::Error for $enum_name {}

        impl std::fmt::Display for $enum_name {
            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
                write!(f, "{:?}", self)
            }
        }
    };
}

/// This macro helps generate types, From impls, etc. for a successful response
/// from a Poem endpoint. It generates a response type that only has the
/// specified response codes, which is then reflected in the OpenAPI spec.
/// See the comments in the macro for an explanation of what is happening.
#[macro_export]
macro_rules! generate_success_response {
    ($enum_name:ident, $(($status:literal, $name:ident)),*) => {
        // We use the paste crate to allows us to generate the name of the code
        // enum, more on that in the comment above that enum.
        paste::paste! {

        // Generate an enum with name `enum_name`. Iterate through each of the
        // response codes, generating a variant for each with the given name
        // and status code.
        #[derive(poem_openapi::ApiResponse)]
        pub enum $enum_name<T: poem_openapi::types::ToJSON + Send + Sync> {
            $(
            #[oai(status = $status)]
            $name(
                $crate::response::AptosResponseContent<T>,
                #[oai(header = "X-Aptos-Chain-Id")] u16,
                // We use just regular u64 here instead of U64 since all header
                // values are implicitly strings anyway.
                #[oai(header = "X-Aptos-Ledger-Version")] u64,
                #[oai(header = "X-Aptos-Ledger-Oldest-Version")] u64,
                #[oai(header = "X-Aptos-Ledger-TimestampUsec")] u64,
                #[oai(header = "X-Aptos-Epoch")] u64,
                #[oai(header = "X-Aptos-Block-Height")] u64,
                #[oai(header = "X-Aptos-Oldest-Block-Height")] u64,
            ),
            )*
        }

        // Generate an enum that captures all the different status codes that
        // this response type supports. To explain this funky syntax, if you
        // named the main enum MyResponse, this would become MyResponseCode.
        pub enum [<$enum_name Status>] {
            $(
            $name,
            )*
        }

        // Generate a From impl that builds a response from AptosResponseContent.
        // Each variant in the main enum takes in the same argument, so the macro
        // is really just helping us enumerate and build each variant. We use this
        // in the other From impls.
        impl <T: poem_openapi::types::ToJSON + Send + Sync> From<($crate::response::AptosResponseContent<T>, &aptos_api_types::LedgerInfo, [<$enum_name Status>])>
            for $enum_name<T>
        {
            fn from(
                (value, ledger_info, status): (
                    $crate::response::AptosResponseContent<T>,
                    &aptos_api_types::LedgerInfo,
                    [<$enum_name Status>]
                ),
            ) -> Self {
                match status {
                    $(
                    [<$enum_name Status>]::$name => {
                        $enum_name::$name(
                            value,
                            ledger_info.chain_id as u16,
                            ledger_info.ledger_version.into(),
                            ledger_info.oldest_ledger_version.into(),
                            ledger_info.ledger_timestamp.into(),
                            ledger_info.epoch.into(),
                            ledger_info.block_height.into(),
                            ledger_info.oldest_block_height.into(),
                        )
                    },
                    )*
                }
            }
        }

        // Generate a From impl that builds a response from a Json<T> and friends.
        impl<T: poem_openapi::types::ToJSON + Send + Sync> From<(poem_openapi::payload::Json<T>, &aptos_api_types::LedgerInfo, [<$enum_name Status>])>
            for $enum_name<T>
        {
            fn from(
                (value, ledger_info, status): (poem_openapi::payload::Json<T>, &aptos_api_types::LedgerInfo, [<$enum_name Status>]),
            ) -> Self {
                let content = $crate::response::AptosResponseContent::Json(value);
                Self::from((content, ledger_info, status))
            }
        }

        // Generate a From impl that builds a response from a Bcs<Vec<u8>> and friends.
        impl<T: poem_openapi::types::ToJSON + Send + Sync> From<($crate::bcs_payload::Bcs, &aptos_api_types::LedgerInfo, [<$enum_name Status>])>
            for $enum_name<T>
        {
            fn from(
                (value, ledger_info, status): (
                    $crate::bcs_payload::Bcs,
                    &aptos_api_types::LedgerInfo,
                    [<$enum_name Status>]
                ),
            ) -> Self {
                let content = $crate::response::AptosResponseContent::Bcs(value);
                Self::from((content, ledger_info, status))
            }
        }

        // Generate a TryFrom impl that builds a response from a T, an AcceptType,
        // and all the other usual suspects. It expects to be called with a generic
        // parameter E: InternalError, with which we can build an internal error
        // response in case the BCS serialization fails.
        impl<T: poem_openapi::types::ToJSON + Send + Sync + serde::Serialize> $enum_name<T> {
            pub fn try_from_rust_value<E: InternalError>(
                (value, ledger_info, status, accept_type): (
                    T,
                    &aptos_api_types::LedgerInfo,
                    [<$enum_name Status>],
                    &$crate::accept_type::AcceptType
                ),
            ) -> Result<Self, E> {
                match accept_type {
                    AcceptType::Bcs => Ok(Self::from((
                        $crate::bcs_payload::Bcs(
                            bcs::to_bytes(&value)
                                .map_err(|e| E::internal(e.into()).error_code(aptos_api_types::AptosErrorCode::BcsSerializationError))?
                        ),
                        ledger_info,
                        status
                    ))),
                    AcceptType::Json => Ok(Self::from((
                        poem_openapi::payload::Json(value),
                        ledger_info,
                        status
                    ))),
                }
            }

           pub fn try_from_json<E: InternalError>(
                (value, ledger_info, status): (
                    T,
                    &aptos_api_types::LedgerInfo,
                    [<$enum_name Status>],
                ),
            ) -> Result<Self, E> {
               Ok(Self::from((
                    poem_openapi::payload::Json(value),
                    ledger_info,
                    status
               )))
            }

            pub fn try_from_bcs<B: serde::Serialize, E: InternalError>(
                (value, ledger_info, status): (
                    B,
                    &aptos_api_types::LedgerInfo,
                    [<$enum_name Status>],
                ),
            ) -> Result<Self, E> {
               Ok(Self::from((
                    $crate::bcs_payload::Bcs(
                        bcs::to_bytes(&value)
                            .map_err(|e| E::internal(e.into()).error_code(aptos_api_types::AptosErrorCode::BcsSerializationError))?
                    ),
                    ledger_info,
                    status
               )))
            }

            pub fn try_from_encoded<E: InternalError>(
                (value, ledger_info, status): (
                    Vec<u8>,
                    &aptos_api_types::LedgerInfo,
                    [<$enum_name Status>],
                ),
            ) -> Result<Self, E> {
               Ok(Self::from((
                    $crate::bcs_payload::Bcs(
                        value
                    ),
                    ledger_info,
                    status
               )))
            }
        }
        }
    };
}

// Generate a success response that only has an option for 200.
generate_success_response!(BasicResponse, (200, Ok));

// Generate traits defining a "from" function for each of these status types.
// The error response then impls these traits for each status type they mention.
generate_error_traits!(
    BadRequest,
    NotFound,
    PayloadTooLarge,
    Internal,
    InsufficientStorage,
    ServiceUnavailable
);

// Generate an error response that only has options for 400 and 500.
generate_error_response!(BasicError, (400, BadRequest), (500, Internal));

// This type just simplifies using BasicResponse and BasicError together.
pub type BasicResult<T> = poem::Result<BasicResponse<T>, BasicError>;

// As above but with 404.
generate_error_response!(
    BasicErrorWith404,
    (400, BadRequest),
    (404, NotFound),
    (500, Internal)
);
pub type BasicResultWith404<T> = poem::Result<BasicResponse<T>, BasicErrorWith404>;

// Just this one helper for a specific kind of 404.
pub fn build_not_found<S: Display, E: NotFoundError>(
    resource: &str,
    identifier: S,
    ledger_version: u64,
) -> E {
    E::not_found_str(&format!("{} not found by {}", resource, identifier))
        .aptos_ledger_version(ledger_version)
}
<|endoftext|>


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

use crate::router::{Handler, HandlerObj, ResponseFuture};
use futures::future::ok;
use hyper::header::{HeaderValue, AUTHORIZATION, WWW_AUTHENTICATE};
use hyper::{Body, Request, Response, StatusCode};
use ring::constant_time::verify_slices_are_equal;

// Basic Authentication Middleware
pub struct BasicAuthMiddleware {
	api_basic_auth: String,
	basic_realm: String,
}

impl BasicAuthMiddleware {
	pub fn new(api_basic_auth: String, basic_realm: String) -> BasicAuthMiddleware {
		BasicAuthMiddleware {
			api_basic_auth,
			basic_realm,
		}
	}
}

impl Handler for BasicAuthMiddleware {
	fn call(
		&self,
		req: Request<Body>,
		mut handlers: Box<dyn Iterator<Item = HandlerObj>>,
	) -> ResponseFuture {
		if req.headers().contains_key(AUTHORIZATION)
			&& verify_slices_are_equal(
				req.headers()[AUTHORIZATION].as_bytes(),
				&self.api_basic_auth.as_bytes(),
			)
			.is_ok()
		{
			handlers.next().unwrap().call(req, handlers)
		} else {
			// Unauthorized 401
			unauthorized_response(&self.basic_realm)
		}
	}
}

fn unauthorized_response(basic_realm: &str) -> ResponseFuture {
	let response = Response::builder()
		.status(StatusCode::UNAUTHORIZED)
		.header(
			WWW_AUTHENTICATE,
			HeaderValue::from_str(basic_realm).unwrap(),
		)
		.body(Body::empty())
		.unwrap();
	Box::new(ok(response))
}
<commit_msg>Disable authenthication for OPTIONS requests (#2131)<commit_after>// Copyright 2018 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::router::{Handler, HandlerObj, ResponseFuture};
use futures::future::ok;
use hyper::header::{HeaderValue, AUTHORIZATION, WWW_AUTHENTICATE};
use hyper::{Body, Request, Response, StatusCode};
use ring::constant_time::verify_slices_are_equal;

// Basic Authentication Middleware
pub struct BasicAuthMiddleware {
	api_basic_auth: String,
	basic_realm: String,
}

impl BasicAuthMiddleware {
	pub fn new(api_basic_auth: String, basic_realm: String) -> BasicAuthMiddleware {
		BasicAuthMiddleware {
			api_basic_auth,
			basic_realm,
		}
	}
}

impl Handler for BasicAuthMiddleware {
	fn call(
		&self,
		req: Request<Body>,
		mut handlers: Box<dyn Iterator<Item = HandlerObj>>,
	) -> ResponseFuture {
		if req.method().as_str() == "OPTIONS" {
			return handlers.next().unwrap().call(req, handlers);
		}
		if req.headers().contains_key(AUTHORIZATION)
			&& verify_slices_are_equal(
				req.headers()[AUTHORIZATION].as_bytes(),
				&self.api_basic_auth.as_bytes(),
			)
			.is_ok()
		{
			handlers.next().unwrap().call(req, handlers)
		} else {
			// Unauthorized 401
			unauthorized_response(&self.basic_realm)
		}
	}
}

fn unauthorized_response(basic_realm: &str) -> ResponseFuture {
	let response = Response::builder()
		.status(StatusCode::UNAUTHORIZED)
		.header(
			WWW_AUTHENTICATE,
			HeaderValue::from_str(basic_realm).unwrap(),
		)
		.body(Body::empty())
		.unwrap();
	Box::new(ok(response))
}
<|endoftext|>


<commit_before>use nom::IResult;
use ast::Expression;
use std::boxed::Box;

// Such imports are used to allow for the expr_literal macro to work
use parser::number::numeric_literal;
use parser::identifier::identifier_literal;

// Temp do bad stuff
named!(pub expression_literal<Expression>,
    complete!(alt!(numeric_literal | identifier_literal))
);<commit_msg>Remove unused imports<commit_after>use ast::Expression;

// Such imports are used to allow for the expr_literal macro to work
use parser::number::numeric_literal;
use parser::identifier::identifier_literal;

// Temp do bad stuff
named!(pub expression_literal<Expression>,
    complete!(alt!(numeric_literal | identifier_literal))
);<|endoftext|>


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

use chrono::{DateTime, Utc, FixedOffset, TimeZone, Duration, NaiveTime};

use eew::*;

#[derive(PartialEq, Eq, Debug, Clone, Copy, Hash)]
pub enum JMAFormatParseError {
	TooShort,
	Split,
	InvalidPattern,
	InvalidSource,
	InvalidKind,
	InvalidIssueTime,
	InvalidOoccurrenceTime,
	InvalidId,
	InvalidStatus,
	InvalidNumber,
	UnknownEpicenter,
	InvalidLL,
	InvalidDepth,
	InvalidMagnitude,
	InvalidMaximumIntensity,
	InvalidEpicenterAccuracy,
	InvalidDepthAccuracy,
	InvalidMagnitudeAccuracy,
	InvalidEpicenterCategory,
	InvalidWarningStatus,
	InvalidIntensityChange,
	InvalidChangeReason,
	InvalidWaveStatus,
	InvalidEBI,
	PrematureEOS,
}

fn parse_datetime(datetime_text: &[u8]) -> Option<DateTime<Utc>>
{
	let jst: FixedOffset = FixedOffset::east(9 * 3600); // XXX: want to use const keyword...
	const DATETIME_FORMAT: &'static str = "%y%m%d%H%M%S";

	str::from_utf8(&datetime_text).ok().and_then( |converted|
		jst.datetime_from_str(converted, DATETIME_FORMAT).ok().map( |dt|
			dt.with_timezone(&Utc)
		)
	)
}

fn parse_number(number_text: &[u8]) -> Option<u32>
{
	str::from_utf8(&number_text).ok().and_then( |converted|
		converted.parse().ok()
	)
}

fn parse_intensity(intensity_text: &[u8]) -> Option<IntensityClass>
{
	match intensity_text {
		b"01" => Some(IntensityClass::One),
		b"02" => Some(IntensityClass::Two),
		b"03" => Some(IntensityClass::Three),
		b"04" => Some(IntensityClass::Four),
		b"5-" => Some(IntensityClass::FiveLower),
		b"5+" => Some(IntensityClass::FiveUpper),
		b"6-" => Some(IntensityClass::SixLower),
		b"6+" => Some(IntensityClass::SixUpper),
		b"07" => Some(IntensityClass::Seven),
		_ => None
	}
}

fn parse_arrival_time(arrival_text: &[u8], base: &DateTime<Utc>) -> Option<DateTime<Utc>>
{
	let jst: FixedOffset = FixedOffset::east(9 * 3600); // XXX: want to use const keyword...
	const TIME_FORMAT: &'static str = "%H%M%S";

	let adjust = |a_t: NaiveTime| {
		let base_t = base.with_timezone(&jst).time();
		let diff = a_t.signed_duration_since(base_t);
		if diff < Duration::seconds(0) {
			base.checked_add_signed(Duration::days(1) + diff)
		} else {
			base.checked_add_signed(diff)
		}
	};

	return str::from_utf8(&arrival_text).ok().and_then( |converted|
		NaiveTime::parse_from_str(&converted, TIME_FORMAT).ok().and_then( |t|
			adjust(t)
		)
	);
}

pub fn parse_jma_format(text: &[u8],
	epicenter_code_dict: &HashMap<[u8; 3], String>,
	area_code_dict: &HashMap<[u8; 3], String>) -> Result<EEW, JMAFormatParseError>
{
	if text.len() < 140 {
		return Err(JMAFormatParseError::TooShort);
	}

	let issue_pattern = match &text[0..2] {
		b"35" => IssuePattern::IntensityOnly,
		b"36" => IssuePattern::LowAccuracy,
		b"37" => IssuePattern::HighAccuracy,
		b"39" => IssuePattern::Cancel,
		_ => return Err(JMAFormatParseError::InvalidPattern),
	};

	let source = match &text[3..5] {
		b"03" => Source::Tokyo,
		b"04" => Source::Osaka,
		_ => return Err(JMAFormatParseError::InvalidSource)
	};

	let kind = match &text[6..8] {
		b"00" => Kind::Normal,
		b"01" => Kind::Drill,
		b"10" => Kind::Cancel,
		b"11" => Kind::DrillCancel,
		b"20" => Kind::Reference,
		b"30" => Kind::Trial,
		_ => return Err(JMAFormatParseError::InvalidKind)
	};

	let issued_at = try!(parse_datetime(&text[9..21]).ok_or(JMAFormatParseError::InvalidIssueTime));

	// TODO: accept split telegrams
	if text[24] != b'1' {
		return Err(JMAFormatParseError::Split);
	}

	let occurred_at = try!(parse_datetime(&text[26..38]).ok_or(JMAFormatParseError::InvalidOoccurrenceTime));

	let id = try!(str::from_utf8(&text[39..55]).map_err(|_| JMAFormatParseError::InvalidId));

	let status = match text[59] {
		b'0' => Status::Normal,
		b'6' => Status::Correction,
		b'7' => Status::CancelCorrection,
		b'8' => Status::LastWithCorrection,
		b'9' => Status::Last,
		b'/' => Status::Unknown,
		_ => return Err(JMAFormatParseError::InvalidStatus)
	};

	// we don't accept an EEW which has no telegram number
	let number = try!(parse_number(&text[60..62]).ok_or(JMAFormatParseError::InvalidNumber));

	if issue_pattern == IssuePattern::Cancel {

		return Ok(EEW {
			issue_pattern: issue_pattern,
			source: source,
			kind: kind,
			issued_at: issued_at,
			occurred_at: occurred_at,
			id: id.to_owned(),
			status: status,
			number: number,
			detail: None,
		});
	}
	let epicenter_name = match epicenter_code_dict.get(&text[86..89]) {
		Some(s) => s.clone(),
		None => return Err(JMAFormatParseError::UnknownEpicenter)
	};

	let lat_value = match parse_number(&text[91..94]) {
		Some(v) => (v as f32) / 10.0,
		None => return Err(JMAFormatParseError::InvalidLL)
	};

	let lat = match text[90] {
		b'N' =>  lat_value,
		b'S' => -lat_value,
		_ => return Err(JMAFormatParseError::InvalidLL)
	};

	let lon_value = match parse_number(&text[96..100]) {
		Some(v) => (v as f32) / 10.0,
		None => return Err(JMAFormatParseError::InvalidLL)
	};

	let lon = match text[95] {
		b'E' =>  lon_value,
		b'W' => -lon_value,
		_ => return Err(JMAFormatParseError::InvalidLL)
	};

	let depth = {
		let t = &text[101..104];
		match parse_number(t) {
			Some(v) => Some(v as f32),
			None => match t {
				b"///" => None,
				_ => return Err(JMAFormatParseError::InvalidDepth)
			}
		}
	};

	let magnitude = {
		let t = &text[105..107];
		match parse_number(t) {
			Some(v) => Some((v as f32) / 10.0),
			None => match t {
				b"//" => None,
				_ => return Err(JMAFormatParseError::InvalidMagnitude)
			}
		}
	};

	let maximum_intensity = {
		let t = &text[108..110];
		match parse_intensity(t) {
			Some(v) => Some(v),
			None => match t {
				b"//" => None,
				_ => return Err(JMAFormatParseError::InvalidMaximumIntensity)
			}
		}
	};

	let epicenter_accuracy = match text[113] {
		b'1' => EpicenterAccuracy::Single,
		b'2' => EpicenterAccuracy::Territory,
		b'3' => EpicenterAccuracy::GridSearchLow,
		b'4' => EpicenterAccuracy::GridSearchHigh,
		b'5' => EpicenterAccuracy::NIEDLow,
		b'6' => EpicenterAccuracy::NIEDHigh,
		b'7' => EpicenterAccuracy::EPOSLow,
		b'8' => EpicenterAccuracy::EPOSHigh,
		b'/' => EpicenterAccuracy::Unknown,
		_ => return Err(JMAFormatParseError::InvalidEpicenterAccuracy)
	};

	let depth_accuracy = match text[114] {
		b'1' => DepthAccuracy::Single,
		b'2' => DepthAccuracy::Territory,
		b'3' => DepthAccuracy::GridSearchLow,
		b'4' => DepthAccuracy::GridSearchHigh,
		b'5' => DepthAccuracy::NIEDLow,
		b'6' => DepthAccuracy::NIEDHigh,
		b'7' => DepthAccuracy::EPOSLow,
		b'8' => DepthAccuracy::EPOSHigh,
		b'/' => DepthAccuracy::Unknown,
		_ => return Err(JMAFormatParseError::InvalidDepthAccuracy)
	};

	let magnitude_accuracy = match text[115] {
		b'2' => MagnitudeAccuracy::NIED,
		b'3' => MagnitudeAccuracy::PWave,
		b'4' => MagnitudeAccuracy::PSMixed,
		b'5' => MagnitudeAccuracy::SWave,
		b'6' => MagnitudeAccuracy::EPOS,
		b'8' => MagnitudeAccuracy::Level,
		b'/' => MagnitudeAccuracy::Unknown,
		_ => return Err(JMAFormatParseError::InvalidMagnitudeAccuracy)
	};

	let epicenter_category = match text[121] {
		b'0' => EpicenterCategory::Land,
		b'1' => EpicenterCategory::Sea,
		b'/' => EpicenterCategory::Unknown,
		_ => return Err(JMAFormatParseError::InvalidEpicenterCategory)
	};

	let warning_status = match text[122] {
		b'0' => WarningStatus::Forecast,
		b'1' => WarningStatus::Alert,
		b'/' => WarningStatus::Unknown,
		_ => return Err(JMAFormatParseError::InvalidWarningStatus)
	};

	let intensity_change = match text[129] {
		b'0' => IntensityChange::Same,
		b'1' => IntensityChange::Up,
		b'2' => IntensityChange::Down,
		b'/' => IntensityChange::Unknown,
		_ => return Err(JMAFormatParseError::InvalidIntensityChange)
	};

	let change_reason = match text[130] {
		b'0' => ChangeReason::Nothing,
		b'1' => ChangeReason::Magnitude,
		b'2' => ChangeReason::Epicenter,
		b'3' => ChangeReason::Mixed,
		b'4' => ChangeReason::Depth,
		b'/' => ChangeReason::Unknown,
		_ => return Err(JMAFormatParseError::InvalidChangeReason)
	};

	let mut area_info = vec! {};

	if &text[135..138] == b"EBI" {

		const EBI_PART_LEN: usize = 20;
		let mut it = 138;

		while it + EBI_PART_LEN < text.len() {

			if &text[(it+1)..(it+6)] == b"9999=" {
				break;
			}

			let part = &text[it..(it + EBI_PART_LEN)];

			let area_name = match area_code_dict.get(&part[1..4]) {
				Some(s) => s.clone(),
				None => return Err(JMAFormatParseError::InvalidEBI)
			};

			let left_intensity =
				parse_intensity(&part[6..8]).ok_or(JMAFormatParseError::InvalidEBI)?;

			let right_intensity = {
				let t = &part[8..10];
				match parse_intensity(t) {
					Some(v) => Some(v),
					None => match t {
						b"//" => None,
						_ => return Err(JMAFormatParseError::InvalidEBI)
					}
				}
			};

			let (minimum_intensity, maximum_intensity) = match right_intensity {
				Some(v) => (v, Some(left_intensity)),
				None => (left_intensity, None)
			};

			let reach_at = {
				let t = &part[11..17];
				match parse_arrival_time(t, &occurred_at) {
					Some(v) => Some(v),
					None => match t {
						b"//////" => None,
						_ => return Err(JMAFormatParseError::InvalidEBI)
					}
				}
			};

			let local_warning_status = match part[18] {
				b'0' => WarningStatus::Forecast,
				b'1' => WarningStatus::Alert,
				b'/' => WarningStatus::Unknown,
				_ => return Err(JMAFormatParseError::InvalidEBI)
			};

			let wave_status = match part[19] {
				b'0' => WaveStatus::Unreached,
				b'1' => WaveStatus::Reached,
				b'/' => WaveStatus::Unknown,
				_ => return Err(JMAFormatParseError::InvalidEBI)
			};

			let area_eew = AreaEEW {
				area_name: area_name,
				minimum_intensity: minimum_intensity,
				maximum_intensity: maximum_intensity,
				reach_at: reach_at,
				warning_status: local_warning_status,
				wave_status: wave_status,
			};

			area_info.push(area_eew);

			it += EBI_PART_LEN;
		}

		if it + 5 >= text.len() || &text[(it+1)..(it+6)] != b"9999=" {
			return Err(JMAFormatParseError::PrematureEOS);
		}
	}

	let detail = EEWDetail {

		epicenter_name: epicenter_name,
		epicenter: (lat, lon),
		depth: depth,
		magnitude: magnitude,
		maximum_intensity: maximum_intensity,
		epicenter_accuracy: epicenter_accuracy,
		depth_accuracy: depth_accuracy,
		magnitude_accuracy: magnitude_accuracy,
		epicenter_category: epicenter_category,
		warning_status: warning_status,
		intensity_change: intensity_change,
		change_reason: change_reason,

		area_info: area_info
	};

	return Ok( EEW {
		issue_pattern: issue_pattern,
		source: source,
		kind: kind,
		issued_at: issued_at,
		occurred_at: occurred_at,
		id: id.to_owned(),
		status: status,
		number: number,
		detail: Some(detail),
	});
}
<commit_msg>Introduce Unknown{Epicenter,Area}Code errors<commit_after>use std::str;
use std::collections::HashMap;

use chrono::{DateTime, Utc, FixedOffset, TimeZone, Duration, NaiveTime};

use eew::*;

#[derive(PartialEq, Eq, Debug, Clone, Copy, Hash)]
pub enum JMAFormatParseError {
	TooShort,
	Split,
	InvalidPattern,
	InvalidSource,
	InvalidKind,
	InvalidIssueTime,
	InvalidOoccurrenceTime,
	InvalidId,
	InvalidStatus,
	InvalidNumber,
	UnknownEpicenterCode,
	InvalidLL,
	InvalidDepth,
	InvalidMagnitude,
	InvalidMaximumIntensity,
	InvalidEpicenterAccuracy,
	InvalidDepthAccuracy,
	InvalidMagnitudeAccuracy,
	InvalidEpicenterCategory,
	InvalidWarningStatus,
	InvalidIntensityChange,
	InvalidChangeReason,
	InvalidWaveStatus,
	InvalidEBI,
	PrematureEOS,
	UnknownAreaCode,
}

fn parse_datetime(datetime_text: &[u8]) -> Option<DateTime<Utc>>
{
	let jst: FixedOffset = FixedOffset::east(9 * 3600); // XXX: want to use const keyword...
	const DATETIME_FORMAT: &'static str = "%y%m%d%H%M%S";

	str::from_utf8(&datetime_text).ok().and_then( |converted|
		jst.datetime_from_str(converted, DATETIME_FORMAT).ok().map( |dt|
			dt.with_timezone(&Utc)
		)
	)
}

fn parse_number(number_text: &[u8]) -> Option<u32>
{
	str::from_utf8(&number_text).ok().and_then( |converted|
		converted.parse().ok()
	)
}

fn parse_intensity(intensity_text: &[u8]) -> Option<IntensityClass>
{
	match intensity_text {
		b"01" => Some(IntensityClass::One),
		b"02" => Some(IntensityClass::Two),
		b"03" => Some(IntensityClass::Three),
		b"04" => Some(IntensityClass::Four),
		b"5-" => Some(IntensityClass::FiveLower),
		b"5+" => Some(IntensityClass::FiveUpper),
		b"6-" => Some(IntensityClass::SixLower),
		b"6+" => Some(IntensityClass::SixUpper),
		b"07" => Some(IntensityClass::Seven),
		_ => None
	}
}

fn parse_arrival_time(arrival_text: &[u8], base: &DateTime<Utc>) -> Option<DateTime<Utc>>
{
	let jst: FixedOffset = FixedOffset::east(9 * 3600); // XXX: want to use const keyword...
	const TIME_FORMAT: &'static str = "%H%M%S";

	let adjust = |a_t: NaiveTime| {
		let base_t = base.with_timezone(&jst).time();
		let diff = a_t.signed_duration_since(base_t);
		if diff < Duration::seconds(0) {
			base.checked_add_signed(Duration::days(1) + diff)
		} else {
			base.checked_add_signed(diff)
		}
	};

	return str::from_utf8(&arrival_text).ok().and_then( |converted|
		NaiveTime::parse_from_str(&converted, TIME_FORMAT).ok().and_then( |t|
			adjust(t)
		)
	);
}

pub fn parse_jma_format(text: &[u8],
	epicenter_code_dict: &HashMap<[u8; 3], String>,
	area_code_dict: &HashMap<[u8; 3], String>) -> Result<EEW, JMAFormatParseError>
{
	if text.len() < 140 {
		return Err(JMAFormatParseError::TooShort);
	}

	let issue_pattern = match &text[0..2] {
		b"35" => IssuePattern::IntensityOnly,
		b"36" => IssuePattern::LowAccuracy,
		b"37" => IssuePattern::HighAccuracy,
		b"39" => IssuePattern::Cancel,
		_ => return Err(JMAFormatParseError::InvalidPattern),
	};

	let source = match &text[3..5] {
		b"03" => Source::Tokyo,
		b"04" => Source::Osaka,
		_ => return Err(JMAFormatParseError::InvalidSource)
	};

	let kind = match &text[6..8] {
		b"00" => Kind::Normal,
		b"01" => Kind::Drill,
		b"10" => Kind::Cancel,
		b"11" => Kind::DrillCancel,
		b"20" => Kind::Reference,
		b"30" => Kind::Trial,
		_ => return Err(JMAFormatParseError::InvalidKind)
	};

	let issued_at = try!(parse_datetime(&text[9..21]).ok_or(JMAFormatParseError::InvalidIssueTime));

	// TODO: accept split telegrams
	if text[24] != b'1' {
		return Err(JMAFormatParseError::Split);
	}

	let occurred_at = try!(parse_datetime(&text[26..38]).ok_or(JMAFormatParseError::InvalidOoccurrenceTime));

	let id = try!(str::from_utf8(&text[39..55]).map_err(|_| JMAFormatParseError::InvalidId));

	let status = match text[59] {
		b'0' => Status::Normal,
		b'6' => Status::Correction,
		b'7' => Status::CancelCorrection,
		b'8' => Status::LastWithCorrection,
		b'9' => Status::Last,
		b'/' => Status::Unknown,
		_ => return Err(JMAFormatParseError::InvalidStatus)
	};

	// we don't accept an EEW which has no telegram number
	let number = try!(parse_number(&text[60..62]).ok_or(JMAFormatParseError::InvalidNumber));

	if issue_pattern == IssuePattern::Cancel {

		return Ok(EEW {
			issue_pattern: issue_pattern,
			source: source,
			kind: kind,
			issued_at: issued_at,
			occurred_at: occurred_at,
			id: id.to_owned(),
			status: status,
			number: number,
			detail: None,
		});
	}
	let epicenter_name = match epicenter_code_dict.get(&text[86..89]) {
		Some(s) => s.clone(),
		None => return Err(JMAFormatParseError::UnknownEpicenterCode)
	};

	let lat_value = match parse_number(&text[91..94]) {
		Some(v) => (v as f32) / 10.0,
		None => return Err(JMAFormatParseError::InvalidLL)
	};

	let lat = match text[90] {
		b'N' =>  lat_value,
		b'S' => -lat_value,
		_ => return Err(JMAFormatParseError::InvalidLL)
	};

	let lon_value = match parse_number(&text[96..100]) {
		Some(v) => (v as f32) / 10.0,
		None => return Err(JMAFormatParseError::InvalidLL)
	};

	let lon = match text[95] {
		b'E' =>  lon_value,
		b'W' => -lon_value,
		_ => return Err(JMAFormatParseError::InvalidLL)
	};

	let depth = {
		let t = &text[101..104];
		match parse_number(t) {
			Some(v) => Some(v as f32),
			None => match t {
				b"///" => None,
				_ => return Err(JMAFormatParseError::InvalidDepth)
			}
		}
	};

	let magnitude = {
		let t = &text[105..107];
		match parse_number(t) {
			Some(v) => Some((v as f32) / 10.0),
			None => match t {
				b"//" => None,
				_ => return Err(JMAFormatParseError::InvalidMagnitude)
			}
		}
	};

	let maximum_intensity = {
		let t = &text[108..110];
		match parse_intensity(t) {
			Some(v) => Some(v),
			None => match t {
				b"//" => None,
				_ => return Err(JMAFormatParseError::InvalidMaximumIntensity)
			}
		}
	};

	let epicenter_accuracy = match text[113] {
		b'1' => EpicenterAccuracy::Single,
		b'2' => EpicenterAccuracy::Territory,
		b'3' => EpicenterAccuracy::GridSearchLow,
		b'4' => EpicenterAccuracy::GridSearchHigh,
		b'5' => EpicenterAccuracy::NIEDLow,
		b'6' => EpicenterAccuracy::NIEDHigh,
		b'7' => EpicenterAccuracy::EPOSLow,
		b'8' => EpicenterAccuracy::EPOSHigh,
		b'/' => EpicenterAccuracy::Unknown,
		_ => return Err(JMAFormatParseError::InvalidEpicenterAccuracy)
	};

	let depth_accuracy = match text[114] {
		b'1' => DepthAccuracy::Single,
		b'2' => DepthAccuracy::Territory,
		b'3' => DepthAccuracy::GridSearchLow,
		b'4' => DepthAccuracy::GridSearchHigh,
		b'5' => DepthAccuracy::NIEDLow,
		b'6' => DepthAccuracy::NIEDHigh,
		b'7' => DepthAccuracy::EPOSLow,
		b'8' => DepthAccuracy::EPOSHigh,
		b'/' => DepthAccuracy::Unknown,
		_ => return Err(JMAFormatParseError::InvalidDepthAccuracy)
	};

	let magnitude_accuracy = match text[115] {
		b'2' => MagnitudeAccuracy::NIED,
		b'3' => MagnitudeAccuracy::PWave,
		b'4' => MagnitudeAccuracy::PSMixed,
		b'5' => MagnitudeAccuracy::SWave,
		b'6' => MagnitudeAccuracy::EPOS,
		b'8' => MagnitudeAccuracy::Level,
		b'/' => MagnitudeAccuracy::Unknown,
		_ => return Err(JMAFormatParseError::InvalidMagnitudeAccuracy)
	};

	let epicenter_category = match text[121] {
		b'0' => EpicenterCategory::Land,
		b'1' => EpicenterCategory::Sea,
		b'/' => EpicenterCategory::Unknown,
		_ => return Err(JMAFormatParseError::InvalidEpicenterCategory)
	};

	let warning_status = match text[122] {
		b'0' => WarningStatus::Forecast,
		b'1' => WarningStatus::Alert,
		b'/' => WarningStatus::Unknown,
		_ => return Err(JMAFormatParseError::InvalidWarningStatus)
	};

	let intensity_change = match text[129] {
		b'0' => IntensityChange::Same,
		b'1' => IntensityChange::Up,
		b'2' => IntensityChange::Down,
		b'/' => IntensityChange::Unknown,
		_ => return Err(JMAFormatParseError::InvalidIntensityChange)
	};

	let change_reason = match text[130] {
		b'0' => ChangeReason::Nothing,
		b'1' => ChangeReason::Magnitude,
		b'2' => ChangeReason::Epicenter,
		b'3' => ChangeReason::Mixed,
		b'4' => ChangeReason::Depth,
		b'/' => ChangeReason::Unknown,
		_ => return Err(JMAFormatParseError::InvalidChangeReason)
	};

	let mut area_info = vec! {};

	if &text[135..138] == b"EBI" {

		const EBI_PART_LEN: usize = 20;
		let mut it = 138;

		while it + EBI_PART_LEN < text.len() {

			if &text[(it+1)..(it+6)] == b"9999=" {
				break;
			}

			let part = &text[it..(it + EBI_PART_LEN)];

			let area_name = match area_code_dict.get(&part[1..4]) {
				Some(s) => s.clone(),
				None => return Err(JMAFormatParseError::UnknownAreaCode)
			};

			let left_intensity =
				parse_intensity(&part[6..8]).ok_or(JMAFormatParseError::InvalidEBI)?;

			let right_intensity = {
				let t = &part[8..10];
				match parse_intensity(t) {
					Some(v) => Some(v),
					None => match t {
						b"//" => None,
						_ => return Err(JMAFormatParseError::InvalidEBI)
					}
				}
			};

			let (minimum_intensity, maximum_intensity) = match right_intensity {
				Some(v) => (v, Some(left_intensity)),
				None => (left_intensity, None)
			};

			let reach_at = {
				let t = &part[11..17];
				match parse_arrival_time(t, &occurred_at) {
					Some(v) => Some(v),
					None => match t {
						b"//////" => None,
						_ => return Err(JMAFormatParseError::InvalidEBI)
					}
				}
			};

			let local_warning_status = match part[18] {
				b'0' => WarningStatus::Forecast,
				b'1' => WarningStatus::Alert,
				b'/' => WarningStatus::Unknown,
				_ => return Err(JMAFormatParseError::InvalidEBI)
			};

			let wave_status = match part[19] {
				b'0' => WaveStatus::Unreached,
				b'1' => WaveStatus::Reached,
				b'/' => WaveStatus::Unknown,
				_ => return Err(JMAFormatParseError::InvalidEBI)
			};

			let area_eew = AreaEEW {
				area_name: area_name,
				minimum_intensity: minimum_intensity,
				maximum_intensity: maximum_intensity,
				reach_at: reach_at,
				warning_status: local_warning_status,
				wave_status: wave_status,
			};

			area_info.push(area_eew);

			it += EBI_PART_LEN;
		}

		if it + 5 >= text.len() || &text[(it+1)..(it+6)] != b"9999=" {
			return Err(JMAFormatParseError::PrematureEOS);
		}
	}

	let detail = EEWDetail {

		epicenter_name: epicenter_name,
		epicenter: (lat, lon),
		depth: depth,
		magnitude: magnitude,
		maximum_intensity: maximum_intensity,
		epicenter_accuracy: epicenter_accuracy,
		depth_accuracy: depth_accuracy,
		magnitude_accuracy: magnitude_accuracy,
		epicenter_category: epicenter_category,
		warning_status: warning_status,
		intensity_change: intensity_change,
		change_reason: change_reason,

		area_info: area_info
	};

	return Ok( EEW {
		issue_pattern: issue_pattern,
		source: source,
		kind: kind,
		issued_at: issued_at,
		occurred_at: occurred_at,
		id: id.to_owned(),
		status: status,
		number: number,
		detail: Some(detail),
	});
}
<|endoftext|>


<commit_before>use story_builder::article_provider::*;
use story_builder::story_builder::StoryBuilder;

struct TestArticle {}

impl Article for TestArticle {
    fn get_related(&self) -> Vec<String> {
        vec![]
    }
}

struct TestArticleProvider {}

impl TestArticleProvider {
    fn expected_suggestion(&self) -> String {
        let mut expected = String::from("Cannot find wikipedia article for <not-found>, try one of the following suggestions:\r\n");
        expected.push_str("- Suggestion 1\r\n");
        expected.push_str("- Suggestion 2\r\n");
        expected.push_str("- Suggestion 3\r\n");
        expected
    }
    fn expected_story(&self) -> String {
        let mut expected = String::from("Paragraph of topic 1\r\n");
        expected.push_str("Paragraph of topic 2\r\n");
        expected.push_str("Paragraph of topic 3\r\n");
        expected.push_str("Paragraph of topic 4\r\n");
        expected.push_str("Paragraph of topic 5\r\n");
        expected.push_str("Paragraph of topic 6\r\n");
        expected
    }
}

impl ArticleProvider for TestArticleProvider {
    /// Returns a Some(Box<Article>) if the article is found, None otherwise.
    fn get(&self, topic: &str) -> Option<Box<Article>> {
        match topic {
            "found" => Some(Box::new(TestArticle {})),
            "not-found" => None,
            _ => {
                // Build a fake article and return:
                Some(Box::new (TestArticle {

                }))
                },
            }
        }
        /// Returns a Vector of topics that might be related to the topic entered.
        fn search(&self, topic: &str) -> Vec<String> {
            vec!["Suggestion 1".to_owned(),
            "Suggestion 2".to_owned(),
            "Suggestion 3".to_owned()]
        }
    }

    #[test]
    /// For: build_suggestions_msg
    fn build_suggestions_msg_test() {
        let provider = TestArticleProvider {};
        let story_builder = StoryBuilder::new(&provider);

        assert_eq!(story_builder.build_suggestions_msg("not-found"), provider.expected_suggestion());
    }

    #[test]
    /// For: build_story
    fn build_story_cannot_find_first_article_suggest() {
        let provider = TestArticleProvider {};
        let story_builder = StoryBuilder::new(&provider);
        assert_eq!(story_builder.build_story("not-found", "found"), Err(provider.expected_suggestion()));
    }

    #[test]
    /// For: build_story
    fn build_story_cannot_find_second_article_suggest() {
        let provider = TestArticleProvider {};
        let story_builder = StoryBuilder::new(&provider);
        assert_eq!(story_builder.build_story("found", "not-found"), Err(provider.expected_suggestion()));
    }
<commit_msg>Fixed indentation.<commit_after>use story_builder::article_provider::*;
use story_builder::story_builder::StoryBuilder;

struct TestArticle {}

impl Article for TestArticle {
    fn get_related(&self) -> Vec<String> {
        vec![]
    }
}

struct TestArticleProvider {}

impl TestArticleProvider {
    fn expected_suggestion(&self) -> String {
        let mut expected = String::from("Cannot find wikipedia article for <not-found>, try one of the following suggestions:\r\n");
        expected.push_str("- Suggestion 1\r\n");
        expected.push_str("- Suggestion 2\r\n");
        expected.push_str("- Suggestion 3\r\n");
        expected
    }
    fn expected_story(&self) -> String {
        let mut expected = String::from("Paragraph of topic 1\r\n");
        expected.push_str("Paragraph of topic 2\r\n");
        expected.push_str("Paragraph of topic 3\r\n");
        expected.push_str("Paragraph of topic 4\r\n");
        expected.push_str("Paragraph of topic 5\r\n");
        expected.push_str("Paragraph of topic 6\r\n");
        expected
    }
}

impl ArticleProvider for TestArticleProvider {
    /// Returns a Some(Box<Article>) if the article is found, None otherwise.
    fn get(&self, topic: &str) -> Option<Box<Article>> {
        match topic {
            "found" => Some(Box::new(TestArticle {})),
            "not-found" => None,
            _ => {
                // Build a fake article and return:
                Some(Box::new (TestArticle {

                }))
                },
            }
        }
        /// Returns a Vector of topics that might be related to the topic entered.
        fn search(&self, topic: &str) -> Vec<String> {
            vec!["Suggestion 1".to_owned(),
            "Suggestion 2".to_owned(),
            "Suggestion 3".to_owned()]
        }
    }

#[test]
/// For: build_suggestions_msg
fn build_suggestions_msg_test() {
    let provider = TestArticleProvider {};
    let story_builder = StoryBuilder::new(&provider);

    assert_eq!(story_builder.build_suggestions_msg("not-found"), provider.expected_suggestion());
}

#[test]
/// For: build_story
fn build_story_cannot_find_first_article_suggest() {
    let provider = TestArticleProvider {};
    let story_builder = StoryBuilder::new(&provider);
    assert_eq!(story_builder.build_story("not-found", "found"), Err(provider.expected_suggestion()));
}

#[test]
/// For: build_story
fn build_story_cannot_find_second_article_suggest() {
    let provider = TestArticleProvider {};
    let story_builder = StoryBuilder::new(&provider);
    assert_eq!(story_builder.build_story("found", "not-found"), Err(provider.expected_suggestion()));
}

#[test]
/// For: build_story
fn build_story_is_working() {
    let provider = TestArticleProvider {};
    let story_builder = StoryBuilder::new(&provider);
    assert_eq!(story_builder.build_story("found", "found"), Err(provider.expected_story()));
}
<|endoftext|>


<commit_before>const END_TAG_LEN: u32 = 8;
const HEADER_LEN: u32 = 24;
pub const MAGIC: u32 = 0xe85250d6;

#[repr(u32)]
pub enum HeaderArch {
      I386 = 0,
	  Mips = 4
}

#[repr(C)]
pub struct Header {
	pub magic: u32,
	pub arch: HeaderArch, pub
	header_length: u32,
	pub checksum: u32,
	pub end_tag: Tag
}

#[repr(C)]
pub struct Info {
	pub length: u32,
	_pad: u32,
	tag_start: Tag
}

#[repr(C)]
#[derive(Debug)]
pub struct Tag
{
	pub ty: TagType,
	length: u32
}

#[repr(u32)]
#[derive(Debug, Eq, PartialEq)]
pub enum TagType {
    End              = 0,
    CommandLine      = 1,
	BootloaderName   = 2,
	Modules          = 3,
	BasicMemInfo     = 4,
	BIOSBootDev      = 5,
	MemoryMap        = 6,
	VBEInfo          = 7,
	FramebufferInfo  = 8,
	ELFSections      = 9,
	APMTable         = 10
}

#[linkage = "external"]
#[link_section = ".multiboot_header"]
pub static HEADER: Header = Header {
	magic: MAGIC,
	arch: HeaderArch::I386,
	header_length: HEADER_LEN,
	checksum: -((MAGIC + 0 + HEADER_LEN) as i32) as u32,
	end_tag: Tag {
		ty: TagType::End,
		length: END_TAG_LEN
    }
};

<commit_msg>Update multiboot.rs<commit_after>const END_TAG_LEN: u32 = 8;
const HEADER_LEN: u32 = 24;
pub const MAGIC: u32 = 0xe85250d6;

#[repr(u32)]
pub enum HeaderArch {
	I386 = 0,
	Mips = 4
}

#[repr(C)]
pub struct Header {
	pub magic: u32,
	pub arch: HeaderArch, pub
	header_length: u32,
	pub checksum: u32,
	pub end_tag: Tag
}

#[repr(C)]
pub struct Info {
	pub length: u32,
	_pad: u32,
	tag_start: Tag
}

#[repr(C)]
#[derive(Debug)]
pub struct Tag {
	pub ty: TagType,
	length: u32
}

#[repr(u32)]
#[derive(Debug, Eq, PartialEq)]
pub enum TagType {
    	End              = 0,
    	CommandLine      = 1,
	BootloaderName   = 2,
	Modules          = 3,
	BasicMemInfo     = 4,
	BIOSBootDev      = 5,
	MemoryMap        = 6,
	VBEInfo          = 7,
	FramebufferInfo  = 8,
	ELFSections      = 9,
	APMTable         = 10
}

#[linkage = "external"]
#[link_section = ".multiboot_header"]
pub static HEADER: Header = Header {
	magic: MAGIC,
	arch: HeaderArch::I386,
	header_length: HEADER_LEN,
	checksum: -((MAGIC + 0 + HEADER_LEN) as i32) as u32,
	end_tag: Tag {
	ty: TagType::End,
	length: END_TAG_LEN
    }
};

<|endoftext|>


<commit_before>use crate::utils::{in_macro, snippet, span_help_and_lint, SpanlessHash};
use rustc::hir::*;
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use rustc::{declare_tool_lint, impl_lint_pass};
use rustc_data_structures::fx::FxHashMap;

#[derive(Copy, Clone)]
pub struct TraitBounds;

declare_clippy_lint! {
    /// **What it does:** This lint warns about unnecessary type repetitions in trait bounds
    ///
    /// **Why is this bad?** Repeating the type for every bound makes the code
    /// less readable than combining the bounds
    ///
    /// **Example:**
    /// ```rust
    /// pub fn foo<T>(t: T) where T: Copy, T: Clone {}
    /// ```
    ///
    /// Could be written as:
    ///
    /// ```rust
    /// pub fn foo<T>(t: T) where T: Copy + Clone {}
    /// ```
    pub TYPE_REPETITION_IN_BOUNDS,
    complexity,
    "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`"
}

impl_lint_pass!(TraitBounds => [TYPE_REPETITION_IN_BOUNDS]);

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TraitBounds {
    fn check_generics(&mut self, cx: &LateContext<'a, 'tcx>, gen: &'tcx Generics) {
        if in_macro(gen.span) {
            return;
        }
        let hash = |ty| -> u64 {
            let mut hasher = SpanlessHash::new(cx, cx.tables);
            hasher.hash_ty(ty);
            hasher.finish()
        };
        let mut map = FxHashMap::default();
        for bound in &gen.where_clause.predicates {
            if let WherePredicate::BoundPredicate(ref p) = bound {
                let h = hash(&p.bounded_ty);
                if let Some(ref v) = map.insert(h, p.bounds.iter().collect::<Vec<_>>()) {
                    let mut hint_string = format!(
                        "consider combining the bounds: `{}:",
                        snippet(cx, p.bounded_ty.span, "_")
                    );
                    for b in v.iter() {
                        if let GenericBound::Trait(ref poly_trait_ref, _) = b {
                            let path = &poly_trait_ref.trait_ref.path;
                            hint_string.push_str(&format!(" {} +", path));
                        }
                    }
                    for b in p.bounds.iter() {
                        if let GenericBound::Trait(ref poly_trait_ref, _) = b {
                            let path = &poly_trait_ref.trait_ref.path;
                            hint_string.push_str(&format!(" {} +", path));
                        }
                    }
                    hint_string.truncate(hint_string.len() - 2);
                    hint_string.push('`');
                    span_help_and_lint(
                        cx,
                        TYPE_REPETITION_IN_BOUNDS,
                        p.span,
                        "this type has already been used as a bound predicate",
                        &hint_string,
                    );
                }
            }
        }
    }
}
<commit_msg>Made lint pedantic<commit_after>use crate::utils::{in_macro, snippet, span_help_and_lint, SpanlessHash};
use rustc::hir::*;
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use rustc::{declare_tool_lint, impl_lint_pass};
use rustc_data_structures::fx::FxHashMap;

#[derive(Copy, Clone)]
pub struct TraitBounds;

declare_clippy_lint! {
    /// **What it does:** This lint warns about unnecessary type repetitions in trait bounds
    ///
    /// **Why is this bad?** Repeating the type for every bound makes the code
    /// less readable than combining the bounds
    ///
    /// **Example:**
    /// ```rust
    /// pub fn foo<T>(t: T) where T: Copy, T: Clone {}
    /// ```
    ///
    /// Could be written as:
    ///
    /// ```rust
    /// pub fn foo<T>(t: T) where T: Copy + Clone {}
    /// ```
    pub TYPE_REPETITION_IN_BOUNDS,
    pedantic,
    "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`"
}

impl_lint_pass!(TraitBounds => [TYPE_REPETITION_IN_BOUNDS]);

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TraitBounds {
    fn check_generics(&mut self, cx: &LateContext<'a, 'tcx>, gen: &'tcx Generics) {
        if in_macro(gen.span) {
            return;
        }
        let hash = |ty| -> u64 {
            let mut hasher = SpanlessHash::new(cx, cx.tables);
            hasher.hash_ty(ty);
            hasher.finish()
        };
        let mut map = FxHashMap::default();
        for bound in &gen.where_clause.predicates {
            if let WherePredicate::BoundPredicate(ref p) = bound {
                let h = hash(&p.bounded_ty);
                if let Some(ref v) = map.insert(h, p.bounds.iter().collect::<Vec<_>>()) {
                    let mut hint_string = format!(
                        "consider combining the bounds: `{}:",
                        snippet(cx, p.bounded_ty.span, "_")
                    );
                    for b in v.iter() {
                        if let GenericBound::Trait(ref poly_trait_ref, _) = b {
                            let path = &poly_trait_ref.trait_ref.path;
                            hint_string.push_str(&format!(" {} +", path));
                        }
                    }
                    for b in p.bounds.iter() {
                        if let GenericBound::Trait(ref poly_trait_ref, _) = b {
                            let path = &poly_trait_ref.trait_ref.path;
                            hint_string.push_str(&format!(" {} +", path));
                        }
                    }
                    hint_string.truncate(hint_string.len() - 2);
                    hint_string.push('`');
                    span_help_and_lint(
                        cx,
                        TYPE_REPETITION_IN_BOUNDS,
                        p.span,
                        "this type has already been used as a bound predicate",
                        &hint_string,
                    );
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::fmt;

use document::QName;

use super::EvaluationContext;
use super::nodeset::{self,Nodeset};

pub trait NodeTest: fmt::Debug {
    fn test<'a, 'd>(&self, context: &EvaluationContext<'a, 'd>, result: &mut Nodeset<'d>);
}

pub type SubNodeTest = Box<NodeTest + 'static>;

#[derive(Clone,PartialEq,Debug)]
pub struct NameTest {
    pub prefix: Option<String>,
    pub local_part: String,
}

impl NameTest {
    fn matches(&self, context: &EvaluationContext, node_name: QName) -> bool {
        let is_wildcard = self.local_part == "*";

        let test_uri = self.prefix.as_ref().map(|p| {
            // TODO: Error for undefined prefix
            context.namespace_for(p)
                .expect("No namespace for prefix")
        });

        match (is_wildcard, test_uri) {
            (true, None) => true,
            (true, Some(..)) => test_uri == node_name.namespace_uri(),
            _ => {
                test_uri == node_name.namespace_uri() &&
                    self.local_part == node_name.local_part()
            },
        }
    }
}

#[derive(Debug)]
pub struct Attribute {
    name_test: NameTest,
}

impl Attribute {
    pub fn new(name: NameTest) -> Attribute {
        Attribute {
            name_test: name,
        }
    }
}

impl NodeTest for Attribute {
    fn test<'a, 'd>(&self, context: &EvaluationContext<'a, 'd>, result: &mut Nodeset<'d>) {
        if let nodeset::Node::Attribute(ref a) = context.node {
            if self.name_test.matches(context, a.name()) {
                result.add(context.node);
            }
        }
    }
}

#[derive(Debug)]
pub struct Namespace {
    name_test: NameTest,
}

impl Namespace {
    pub fn new(name: NameTest) -> Namespace {
        Namespace {
            name_test: name,
        }
    }
}

impl NodeTest for Namespace {
    fn test<'a, 'd>(&self, context: &EvaluationContext<'a, 'd>, result: &mut Nodeset<'d>) {
        if let nodeset::Node::Namespace(ref ns) = context.node {
            if self.name_test.matches(context, QName::new(ns.prefix())) {
                result.add(context.node);
            }
        }
    }
}

#[derive(Debug)]
pub struct Element {
    name_test: NameTest,
}

impl Element {
    pub fn new(name: NameTest) -> Element {
        Element {
            name_test: name,
        }
    }
}

impl NodeTest for Element {
    fn test<'a, 'd>(&self, context: &EvaluationContext<'a, 'd>, result: &mut Nodeset<'d>) {
        if let nodeset::Node::Element(ref e) = context.node {
            if self.name_test.matches(context, e.name()) {
                result.add(context.node);
            }
        }
    }
}

#[allow(missing_copy_implementations)]
#[derive(Debug)]
pub struct Node;

impl NodeTest for Node {
    fn test<'a, 'd>(&self, context: &EvaluationContext<'a, 'd>, result: &mut Nodeset<'d>) {
        result.add(context.node);
    }
}

#[allow(missing_copy_implementations)]
#[derive(Debug)]
pub struct Text;

impl NodeTest for Text {
    fn test<'a, 'd>(&self, context: &EvaluationContext<'a, 'd>, result: &mut Nodeset<'d>) {
        if let nodeset::Node::Text(_) = context.node {
            result.add(context.node);
        }
    }
}

#[allow(missing_copy_implementations)]
#[derive(Debug)]
pub struct Comment;

impl NodeTest for Comment {
    fn test<'a, 'd>(&self, context: &EvaluationContext<'a, 'd>, result: &mut Nodeset<'d>) {
        if let nodeset::Node::Comment(_) = context.node {
            result.add(context.node);
        }
    }
}

#[derive(Debug)]
pub struct ProcessingInstruction {
    target: Option<String>,
}

impl ProcessingInstruction {
    pub fn new(target: Option<String>) -> ProcessingInstruction {
        ProcessingInstruction { target: target }
    }
}

impl NodeTest for ProcessingInstruction {
    fn test<'a, 'd>(&self, context: &EvaluationContext<'a, 'd>, result: &mut Nodeset<'d>) {
        if let nodeset::Node::ProcessingInstruction(pi) = context.node {
            match self.target {
                Some(ref name) if name == &pi.target() => result.add(context.node),
                Some(_)                                => {},
                None                                   => result.add(context.node),
            }
        }
    }
}

#[cfg(test)]
mod test {
    use std::borrow::ToOwned;
    use std::collections::HashMap;

    use document::{Package,ToQName};
    use document::dom4::{Document,Element,Attribute};

    use super::super::{EvaluationContext,Functions,Variables,Namespaces};
    use super::super::nodeset::Nodeset;

    use super::super::node_test;
    use super::NodeTest;

    struct Setup<'d> {
        doc: Document<'d>,
        functions: Functions,
        variables: Variables<'d>,
        namespaces: Namespaces,
    }

    impl<'d> Setup<'d> {
        fn new(package: &'d Package) -> Setup {
            Setup {
                doc: package.as_document(),
                functions: HashMap::new(),
                variables: HashMap::new(),
                namespaces: HashMap::new(),
            }
        }

        fn register_prefix(&mut self, prefix: &str, namespace_uri: &str) {
            self.namespaces.insert(prefix.to_owned(), namespace_uri.to_owned());
        }

        fn context_for_attribute<'n, N>(&'d self, name: N, val: &str)
                                        -> (Attribute<'d>, EvaluationContext<'d, 'd>)
            where N: ToQName<'n>
        {
            let e = self.doc.create_element("element");
            let a = e.set_attribute_value(name, val);
            let c = EvaluationContext::new(a, &self.functions, &self.variables, &self.namespaces);
            (a, c)
        }

        fn context_for_ns_attribute(&'d mut self, prefix: &str, nsuri: &str, local: &str, value: &str)
                                    -> (Attribute<'d>, EvaluationContext<'d, 'd>)
        {
            self.register_prefix(prefix, nsuri);
            self.context_for_attribute((nsuri, local), value)
        }

        fn context_for_element<'n, N>(&'d self, name: N)
                                  -> (Element<'d>, EvaluationContext<'d, 'd>)
            where N: ToQName<'n>
        {
            let e = self.doc.create_element(name);
            let c = EvaluationContext::new(e, &self.functions, &self.variables, &self.namespaces);
            (e, c)
        }

        fn context_for_ns_element(&'d mut self, prefix: &str, nsuri: &str, local: &str)
                                  -> (Element<'d>, EvaluationContext<'d, 'd>)
        {
            self.register_prefix(prefix, nsuri);
            self.context_for_element((nsuri, local))
        }
    }

    fn run_attribute<'d>(context: &EvaluationContext<'d, 'd>, prefix: Option<&str>, local: &str)
                       -> Nodeset<'d>
    {
        let mut result = Nodeset::new();
        let name = super::NameTest {
            prefix: prefix.map(|p| p.to_owned()),
            local_part: local.to_owned()
        };
        let test = node_test::Attribute::new(name);
        test.test(context, &mut result);
        result
    }

    #[test]
    fn attribute_test_selects_attributes_with_matching_names() {
        let package = Package::new();
        let setup = Setup::new(&package);
        let (attribute, context) = setup.context_for_attribute("hello", "world");

        let result = run_attribute(&context, None, "hello");
        assert_eq!(nodeset![attribute], result);
    }

    #[test]
    fn attribute_test_does_not_select_other_names() {
        let package = Package::new();
        let setup = Setup::new(&package);
        let (_, context) = setup.context_for_attribute("goodbye", "world");

        let result = run_attribute(&context, None, "hello");
        assert_eq!(nodeset![], result);
    }

    #[test]
    fn attribute_test_supports_a_wildcard_match() {
        let package = Package::new();
        let setup = Setup::new(&package);
        let (attribute, context) = setup.context_for_attribute("whatever", "value");

        let result = run_attribute(&context, None, "*");
        assert_eq!(nodeset![attribute], result);
    }

    #[test]
    fn attribute_test_matches_on_namespace_and_name() {
        let package = Package::new();
        let mut setup = Setup::new(&package);
        setup.register_prefix("another", "different-uri");
        let (attribute, context) = setup.context_for_ns_attribute("prefix", "namespace", "name", "value");

        let result = run_attribute(&context, Some("prefix"), "name");
        assert_eq!(nodeset![attribute], result);

        let result = run_attribute(&context, Some("prefix"), "wrong-name");
        assert_eq!(nodeset![], result);

        let result = run_attribute(&context, Some("another"), "name");
        assert_eq!(nodeset![], result);
    }

    #[test]
    fn attribute_test_matches_on_namespace_when_wildcard_with_prefix() {
        let package = Package::new();
        let mut setup = Setup::new(&package);
        setup.register_prefix("another", "different-uri");
        let (attribute, context) = setup.context_for_ns_attribute("prefix", "namespace", "name", "value");

        let result = run_attribute(&context, Some("prefix"), "*");
        assert_eq!(nodeset![attribute], result);

        let result = run_attribute(&context, Some("another"), "*");
        assert_eq!(nodeset![], result);
    }

    #[test]
    fn attribute_test_ignores_namespace_when_wildcard_without_prefix() {
        let package = Package::new();
        let mut setup = Setup::new(&package);
        let (attribute, context) = setup.context_for_ns_attribute("prefix", "namespace", "name", "value");

        let result = run_attribute(&context, None, "*");
        assert_eq!(nodeset![attribute], result);
    }

    #[test]
    fn attribute_test_does_not_match_when_attribute_has_namespace_but_without_prefix() {
        let package = Package::new();
        let mut setup = Setup::new(&package);
        let (_, context) = setup.context_for_ns_attribute("prefix", "namespace", "name", "value");

        let result = run_attribute(&context, None, "name");
        assert_eq!(nodeset![], result);
    }

    fn run_element<'d>(context: &EvaluationContext<'d, 'd>, prefix: Option<&str>, local: &str)
                       -> Nodeset<'d>
    {
        let mut result = Nodeset::new();
        let name = super::NameTest {
            prefix: prefix.map(|p| p.to_owned()),
            local_part: local.to_owned()
        };
        let test = node_test::Element::new(name);
        test.test(context, &mut result);
        result
    }

    #[test]
    fn element_test_selects_nodes_with_matching_names() {
        let package = Package::new();
        let setup = Setup::new(&package);
        let (element, context) = setup.context_for_element("hello");

        let result = run_element(&context, None, "hello");
        assert_eq!(nodeset![element], result);
    }

    #[test]
    fn element_test_does_not_select_other_names() {
        let package = Package::new();
        let setup = Setup::new(&package);
        let (_, context) = setup.context_for_element("goodbye");

        let result = run_element(&context, None, "hello");
        assert_eq!(nodeset![], result);
    }

    #[test]
    fn element_test_supports_a_wildcard_match() {
        let package = Package::new();
        let setup = Setup::new(&package);
        let (element, context) = setup.context_for_element("hello");

        let result = run_element(&context, None, "*");
        assert_eq!(nodeset![element], result);
    }

    #[test]
    fn element_test_matches_on_namespace_and_name() {
        let package = Package::new();
        let mut setup = Setup::new(&package);
        setup.register_prefix("another", "different-uri");
        let (element, context) = setup.context_for_ns_element("prefix", "uri", "name");

        let result = run_element(&context, Some("prefix"), "name");
        assert_eq!(nodeset![element], result);

        let result = run_element(&context, Some("prefix"), "wrong-name");
        assert_eq!(nodeset![], result);

        let result = run_element(&context, Some("another"), "name");
        assert_eq!(nodeset![], result);
    }

    #[test]
    fn element_test_matches_on_namespace_when_wildcard_with_prefix() {
        let package = Package::new();
        let mut setup = Setup::new(&package);
        setup.register_prefix("another", "different-uri");
        let (element, context) = setup.context_for_ns_element("prefix", "uri", "name");

        let result = run_element(&context, Some("prefix"), "*");
        assert_eq!(nodeset![element], result);

        let result = run_element(&context, Some("another"), "*");
        assert_eq!(nodeset![], result);
    }

    #[test]
    fn element_test_ignores_namespace_when_wildcard_without_prefix() {
        let package = Package::new();
        let mut setup = Setup::new(&package);
        let (element, context) = setup.context_for_ns_element("prefix", "uri", "name");

        let result = run_element(&context, None, "*");
        assert_eq!(nodeset![element], result);
    }

    #[test]
    fn element_test_does_not_match_when_element_has_namespace_but_without_prefix() {
        let package = Package::new();
        let mut setup = Setup::new(&package);
        let (_, context) = setup.context_for_ns_element("prefix", "uri", "name");

        let result = run_element(&context, None, "name");
        assert_eq!(nodeset![], result);
    }
}
<commit_msg>ToQName -> IntoQName<commit_after>use std::fmt;

use document::QName;

use super::EvaluationContext;
use super::nodeset::{self,Nodeset};

pub trait NodeTest: fmt::Debug {
    fn test<'a, 'd>(&self, context: &EvaluationContext<'a, 'd>, result: &mut Nodeset<'d>);
}

pub type SubNodeTest = Box<NodeTest + 'static>;

#[derive(Clone,PartialEq,Debug)]
pub struct NameTest {
    pub prefix: Option<String>,
    pub local_part: String,
}

impl NameTest {
    fn matches(&self, context: &EvaluationContext, node_name: QName) -> bool {
        let is_wildcard = self.local_part == "*";

        let test_uri = self.prefix.as_ref().map(|p| {
            // TODO: Error for undefined prefix
            context.namespace_for(p)
                .expect("No namespace for prefix")
        });

        match (is_wildcard, test_uri) {
            (true, None) => true,
            (true, Some(..)) => test_uri == node_name.namespace_uri(),
            _ => {
                test_uri == node_name.namespace_uri() &&
                    self.local_part == node_name.local_part()
            },
        }
    }
}

#[derive(Debug)]
pub struct Attribute {
    name_test: NameTest,
}

impl Attribute {
    pub fn new(name: NameTest) -> Attribute {
        Attribute {
            name_test: name,
        }
    }
}

impl NodeTest for Attribute {
    fn test<'a, 'd>(&self, context: &EvaluationContext<'a, 'd>, result: &mut Nodeset<'d>) {
        if let nodeset::Node::Attribute(ref a) = context.node {
            if self.name_test.matches(context, a.name()) {
                result.add(context.node);
            }
        }
    }
}

#[derive(Debug)]
pub struct Namespace {
    name_test: NameTest,
}

impl Namespace {
    pub fn new(name: NameTest) -> Namespace {
        Namespace {
            name_test: name,
        }
    }
}

impl NodeTest for Namespace {
    fn test<'a, 'd>(&self, context: &EvaluationContext<'a, 'd>, result: &mut Nodeset<'d>) {
        if let nodeset::Node::Namespace(ref ns) = context.node {
            if self.name_test.matches(context, QName::new(ns.prefix())) {
                result.add(context.node);
            }
        }
    }
}

#[derive(Debug)]
pub struct Element {
    name_test: NameTest,
}

impl Element {
    pub fn new(name: NameTest) -> Element {
        Element {
            name_test: name,
        }
    }
}

impl NodeTest for Element {
    fn test<'a, 'd>(&self, context: &EvaluationContext<'a, 'd>, result: &mut Nodeset<'d>) {
        if let nodeset::Node::Element(ref e) = context.node {
            if self.name_test.matches(context, e.name()) {
                result.add(context.node);
            }
        }
    }
}

#[allow(missing_copy_implementations)]
#[derive(Debug)]
pub struct Node;

impl NodeTest for Node {
    fn test<'a, 'd>(&self, context: &EvaluationContext<'a, 'd>, result: &mut Nodeset<'d>) {
        result.add(context.node);
    }
}

#[allow(missing_copy_implementations)]
#[derive(Debug)]
pub struct Text;

impl NodeTest for Text {
    fn test<'a, 'd>(&self, context: &EvaluationContext<'a, 'd>, result: &mut Nodeset<'d>) {
        if let nodeset::Node::Text(_) = context.node {
            result.add(context.node);
        }
    }
}

#[allow(missing_copy_implementations)]
#[derive(Debug)]
pub struct Comment;

impl NodeTest for Comment {
    fn test<'a, 'd>(&self, context: &EvaluationContext<'a, 'd>, result: &mut Nodeset<'d>) {
        if let nodeset::Node::Comment(_) = context.node {
            result.add(context.node);
        }
    }
}

#[derive(Debug)]
pub struct ProcessingInstruction {
    target: Option<String>,
}

impl ProcessingInstruction {
    pub fn new(target: Option<String>) -> ProcessingInstruction {
        ProcessingInstruction { target: target }
    }
}

impl NodeTest for ProcessingInstruction {
    fn test<'a, 'd>(&self, context: &EvaluationContext<'a, 'd>, result: &mut Nodeset<'d>) {
        if let nodeset::Node::ProcessingInstruction(pi) = context.node {
            match self.target {
                Some(ref name) if name == &pi.target() => result.add(context.node),
                Some(_)                                => {},
                None                                   => result.add(context.node),
            }
        }
    }
}

#[cfg(test)]
mod test {
    use std::borrow::ToOwned;
    use std::collections::HashMap;

    use document::{Package,IntoQName};
    use document::dom4::{Document,Element,Attribute};

    use super::super::{EvaluationContext,Functions,Variables,Namespaces};
    use super::super::nodeset::Nodeset;

    use super::super::node_test;
    use super::NodeTest;

    struct Setup<'d> {
        doc: Document<'d>,
        functions: Functions,
        variables: Variables<'d>,
        namespaces: Namespaces,
    }

    impl<'d> Setup<'d> {
        fn new(package: &'d Package) -> Setup {
            Setup {
                doc: package.as_document(),
                functions: HashMap::new(),
                variables: HashMap::new(),
                namespaces: HashMap::new(),
            }
        }

        fn register_prefix(&mut self, prefix: &str, namespace_uri: &str) {
            self.namespaces.insert(prefix.to_owned(), namespace_uri.to_owned());
        }

        fn context_for_attribute<'n, N>(&'d self, name: N, val: &str)
                                        -> (Attribute<'d>, EvaluationContext<'d, 'd>)
            where N: IntoQName<'n>
        {
            let e = self.doc.create_element("element");
            let a = e.set_attribute_value(name, val);
            let c = EvaluationContext::new(a, &self.functions, &self.variables, &self.namespaces);
            (a, c)
        }

        fn context_for_ns_attribute(&'d mut self, prefix: &str, nsuri: &str, local: &str, value: &str)
                                    -> (Attribute<'d>, EvaluationContext<'d, 'd>)
        {
            self.register_prefix(prefix, nsuri);
            self.context_for_attribute((nsuri, local), value)
        }

        fn context_for_element<'n, N>(&'d self, name: N)
                                  -> (Element<'d>, EvaluationContext<'d, 'd>)
            where N: IntoQName<'n>
        {
            let e = self.doc.create_element(name);
            let c = EvaluationContext::new(e, &self.functions, &self.variables, &self.namespaces);
            (e, c)
        }

        fn context_for_ns_element(&'d mut self, prefix: &str, nsuri: &str, local: &str)
                                  -> (Element<'d>, EvaluationContext<'d, 'd>)
        {
            self.register_prefix(prefix, nsuri);
            self.context_for_element((nsuri, local))
        }
    }

    fn run_attribute<'d>(context: &EvaluationContext<'d, 'd>, prefix: Option<&str>, local: &str)
                       -> Nodeset<'d>
    {
        let mut result = Nodeset::new();
        let name = super::NameTest {
            prefix: prefix.map(|p| p.to_owned()),
            local_part: local.to_owned()
        };
        let test = node_test::Attribute::new(name);
        test.test(context, &mut result);
        result
    }

    #[test]
    fn attribute_test_selects_attributes_with_matching_names() {
        let package = Package::new();
        let setup = Setup::new(&package);
        let (attribute, context) = setup.context_for_attribute("hello", "world");

        let result = run_attribute(&context, None, "hello");
        assert_eq!(nodeset![attribute], result);
    }

    #[test]
    fn attribute_test_does_not_select_other_names() {
        let package = Package::new();
        let setup = Setup::new(&package);
        let (_, context) = setup.context_for_attribute("goodbye", "world");

        let result = run_attribute(&context, None, "hello");
        assert_eq!(nodeset![], result);
    }

    #[test]
    fn attribute_test_supports_a_wildcard_match() {
        let package = Package::new();
        let setup = Setup::new(&package);
        let (attribute, context) = setup.context_for_attribute("whatever", "value");

        let result = run_attribute(&context, None, "*");
        assert_eq!(nodeset![attribute], result);
    }

    #[test]
    fn attribute_test_matches_on_namespace_and_name() {
        let package = Package::new();
        let mut setup = Setup::new(&package);
        setup.register_prefix("another", "different-uri");
        let (attribute, context) = setup.context_for_ns_attribute("prefix", "namespace", "name", "value");

        let result = run_attribute(&context, Some("prefix"), "name");
        assert_eq!(nodeset![attribute], result);

        let result = run_attribute(&context, Some("prefix"), "wrong-name");
        assert_eq!(nodeset![], result);

        let result = run_attribute(&context, Some("another"), "name");
        assert_eq!(nodeset![], result);
    }

    #[test]
    fn attribute_test_matches_on_namespace_when_wildcard_with_prefix() {
        let package = Package::new();
        let mut setup = Setup::new(&package);
        setup.register_prefix("another", "different-uri");
        let (attribute, context) = setup.context_for_ns_attribute("prefix", "namespace", "name", "value");

        let result = run_attribute(&context, Some("prefix"), "*");
        assert_eq!(nodeset![attribute], result);

        let result = run_attribute(&context, Some("another"), "*");
        assert_eq!(nodeset![], result);
    }

    #[test]
    fn attribute_test_ignores_namespace_when_wildcard_without_prefix() {
        let package = Package::new();
        let mut setup = Setup::new(&package);
        let (attribute, context) = setup.context_for_ns_attribute("prefix", "namespace", "name", "value");

        let result = run_attribute(&context, None, "*");
        assert_eq!(nodeset![attribute], result);
    }

    #[test]
    fn attribute_test_does_not_match_when_attribute_has_namespace_but_without_prefix() {
        let package = Package::new();
        let mut setup = Setup::new(&package);
        let (_, context) = setup.context_for_ns_attribute("prefix", "namespace", "name", "value");

        let result = run_attribute(&context, None, "name");
        assert_eq!(nodeset![], result);
    }

    fn run_element<'d>(context: &EvaluationContext<'d, 'd>, prefix: Option<&str>, local: &str)
                       -> Nodeset<'d>
    {
        let mut result = Nodeset::new();
        let name = super::NameTest {
            prefix: prefix.map(|p| p.to_owned()),
            local_part: local.to_owned()
        };
        let test = node_test::Element::new(name);
        test.test(context, &mut result);
        result
    }

    #[test]
    fn element_test_selects_nodes_with_matching_names() {
        let package = Package::new();
        let setup = Setup::new(&package);
        let (element, context) = setup.context_for_element("hello");

        let result = run_element(&context, None, "hello");
        assert_eq!(nodeset![element], result);
    }

    #[test]
    fn element_test_does_not_select_other_names() {
        let package = Package::new();
        let setup = Setup::new(&package);
        let (_, context) = setup.context_for_element("goodbye");

        let result = run_element(&context, None, "hello");
        assert_eq!(nodeset![], result);
    }

    #[test]
    fn element_test_supports_a_wildcard_match() {
        let package = Package::new();
        let setup = Setup::new(&package);
        let (element, context) = setup.context_for_element("hello");

        let result = run_element(&context, None, "*");
        assert_eq!(nodeset![element], result);
    }

    #[test]
    fn element_test_matches_on_namespace_and_name() {
        let package = Package::new();
        let mut setup = Setup::new(&package);
        setup.register_prefix("another", "different-uri");
        let (element, context) = setup.context_for_ns_element("prefix", "uri", "name");

        let result = run_element(&context, Some("prefix"), "name");
        assert_eq!(nodeset![element], result);

        let result = run_element(&context, Some("prefix"), "wrong-name");
        assert_eq!(nodeset![], result);

        let result = run_element(&context, Some("another"), "name");
        assert_eq!(nodeset![], result);
    }

    #[test]
    fn element_test_matches_on_namespace_when_wildcard_with_prefix() {
        let package = Package::new();
        let mut setup = Setup::new(&package);
        setup.register_prefix("another", "different-uri");
        let (element, context) = setup.context_for_ns_element("prefix", "uri", "name");

        let result = run_element(&context, Some("prefix"), "*");
        assert_eq!(nodeset![element], result);

        let result = run_element(&context, Some("another"), "*");
        assert_eq!(nodeset![], result);
    }

    #[test]
    fn element_test_ignores_namespace_when_wildcard_without_prefix() {
        let package = Package::new();
        let mut setup = Setup::new(&package);
        let (element, context) = setup.context_for_ns_element("prefix", "uri", "name");

        let result = run_element(&context, None, "*");
        assert_eq!(nodeset![element], result);
    }

    #[test]
    fn element_test_does_not_match_when_element_has_namespace_but_without_prefix() {
        let package = Package::new();
        let mut setup = Setup::new(&package);
        let (_, context) = setup.context_for_ns_element("prefix", "uri", "name");

        let result = run_element(&context, None, "name");
        assert_eq!(nodeset![], result);
    }
}
<|endoftext|>