<commit_before>use xmlhelper::decode;

str_enum! {
  pub enum ShipmentStatus  {
    WORKING,    // - The shipment was created by the seller, but has not yet shipped.
    SHIPPED,    // - The shipment was picked up by the carrier.
    IN_TRANSIT, // - The carrier has notified the Amazon fulfillment center that it is aware of the shipment.
    DELIVERED,  // - The shipment was delivered by the carrier to the Amazon fulfillment center.
    CHECKED_IN, // - The shipment was checked-in at the receiving dock of the Amazon fulfillment center.
    RECEIVING,  // - The shipment has arrived at the Amazon fulfillment center, but not all items have been marked as received.
    CLOSED,     // - The shipment has arrived at the Amazon fulfillment center and all items have been marked as received.
    CANCELLED,  // - The shipment was cancelled by the seller after the shipment was sent to the Amazon fulfillment center.
    DELETED,    // - The shipment was cancelled by the seller before the shipment was sent to the Amazon fulfillment center.
    ERROR,      // - There was an error with the shipment and it was not processed by Amazon.
  }
}

/// Where the seller provided box contents information for a shipment. This is only returned for shipments to US fulfillment centers.
string_map_enum! {
  pub enum BoxContentsSource {
    NONE = "NONE",
    FEED = "FEED",
    _2D_BARCODE = "2D_BARCODE",
    INTERACTIVE = "INTERACTIVE",
  }
}

#[allow(non_snake_case)]
#[derive(Debug, Default, PartialEq)]
pub struct Amount {
  pub CurrencyCode: String,
  pub Value: f64,
}

impl<S: decode::XmlEventStream> decode::FromXMLStream<S> for Amount {
  fn from_xml(s: &mut S) -> decode::Result<Amount> {
    use xmlhelper::decode::{fold_elements, characters};
    fold_elements(s, Amount::default(), |s, record| {
      match s.local_name() {
        "CurrencyCode" => record.CurrencyCode = characters(s)?,
        "Value" => record.Value = characters(s)?,
        _ => {},
      }
      Ok(())
    })
  }
}

/// The manual processing fee per unit and total fee for a shipment.
#[allow(non_snake_case)]
#[derive(Debug, Default, PartialEq)]
pub struct BoxContentsFeeDetails {
  pub TotalUnits: i32,
  pub FeePerUnit: Option<Amount>,
  pub TotalFee: Option<Amount>,
}

impl<S: decode::XmlEventStream> decode::FromXMLStream<S> for BoxContentsFeeDetails {
  fn from_xml(s: &mut S) -> decode::Result<BoxContentsFeeDetails> {
    use xmlhelper::decode::{fold_elements, characters};
    fold_elements(s, BoxContentsFeeDetails::default(), |s, record| {
      match s.local_name() {
        "TotalUnits" => record.TotalUnits = characters(s)?,
        "FeePerUnit" => record.FeePerUnit = Amount::from_xml(s).map(Some)?,
        "TotalFee" => record.TotalFee = Amount::from_xml(s).map(Some)?,
        _ => {},
      }
      Ok(())
    })
  }
}

str_enum! {
  pub enum LabelPrepType {
    NO_LABEL,
    SELLER_LABEL,
    AMAZON_LABEL,
  }
}

/// Postal address information.
#[allow(non_snake_case)]
#[derive(Debug, Default, PartialEq)]
pub struct Address {
  /// The name or business name.
  pub Name: String,
  /// The street address information.
  pub AddressLine1: String,
  ///	Additional street address information, if required.
  pub AddressLine2: String,
  /// The city.
  pub City: String,
  /// The district or county.
  pub DistrictOrCounty: String,
  /// The state or province code.
  pub StateOrProvinceCode: String,
  /// The country code.
  pub CountryCode: String,
  /// The postal code.
  pub PostalCode: String,
}

impl<S: decode::XmlEventStream> decode::FromXMLStream<S> for Address {
  fn from_xml(s: &mut S) -> decode::Result<Address> {
    use xmlhelper::decode::{fold_elements, characters};
    fold_elements(s, Address::default(), |s, record| {
      match s.local_name() {
        "Name" => record.Name = characters(s)?,
        "AddressLine1" => record.AddressLine1 = characters(s)?,
        "AddressLine2" => record.AddressLine2 = characters(s)?,
        "City" => record.City = characters(s)?,
        "DistrictOrCounty" => record.DistrictOrCounty = characters(s)?,
        "StateOrProvinceCode" => record.StateOrProvinceCode = characters(s)?,
        "CountryCode" => record.CountryCode = characters(s)?,
        "PostalCode" => record.PostalCode = characters(s)?,
        _ => {},
      }
      Ok(())
    })
  }
}

/// Information about your inbound shipments. Returned by the `ListInboundShipments` operation.
#[allow(non_snake_case)]
#[derive(Debug, Default, PartialEq)]
pub struct InboundShipmentInfo {
  /// The ShipmentId submitted in the request.
  pub ShipmentId: String,
  /// A unique name that you provide for your inbound shipment.
  pub ShipmentName: String,
  /// Your return address.
  pub ShipFromAddress: Address,
  /// An Amazon fulfillment center identifier created by Amazon.
  pub DestinationFulfillmentCenterId: String,
  /// The type of label preparation that is required for your inbound shipment.
  pub LabelPrepType: Option<LabelPrepType>,
  /// The status of your inbound shipment.
  pub ShipmentStatus: ShipmentStatus,
  /// Indicates whether or not an inbound shipment contains case-packed boxes.
  /// When AreCasesRequired = true for an inbound shipment, all items in the
  /// inbound shipment must be case packed.
  pub AreCasesRequired: bool,
  ///	Date that the shipment must arrive at an Amazon fulfillment center to avoid
  /// delivery promise breaks for pre-ordered items. For more information, see
  /// GetPreorderInfo. Pre-orders are only available in India and Japan.
  pub ConfirmedNeedByDate: String,
  ///	Where the seller provided box contents information for a shipment. This is
  /// only returned for shipments to US fulfillment centers.
  pub BoxContentsSource: Option<BoxContentsSource>,
  /// An estimate of the manual processing fee charged by Amazon for boxes
  /// without box content information. This is only returned when BoxContentsSource is NONE.
  pub EstimatedBoxContentsFee: Option<BoxContentsFeeDetails>,
}

impl<S: decode::XmlEventStream> decode::FromXMLStream<S> for InboundShipmentInfo {
  fn from_xml(s: &mut S) -> decode::Result<InboundShipmentInfo> {
    use xmlhelper::decode::{fold_elements, characters};
    fold_elements(s, InboundShipmentInfo::default(), |s, record| {
      match s.local_name() {
        "ShipmentId" => record.ShipmentId = characters(s)?,
        "ShipmentName" => record.ShipmentName = characters(s)?,
        "ShipFromAddress" => record.ShipFromAddress = Address::from_xml(s)?,
        "DestinationFulfillmentCenterId" => record.DestinationFulfillmentCenterId = characters(s)?,
        "LabelPrepType" => record.LabelPrepType = characters(s).map(Some)?,
        "ShipmentStatus" => record.ShipmentStatus = characters(s)?,
        "AreCasesRequired" => record.AreCasesRequired = characters(s)?,
        "ConfirmedNeedByDate" => record.ConfirmedNeedByDate = characters(s)?,
        "BoxContentsSource" => record.BoxContentsSource = characters(s).map(Some)?,
        "EstimatedBoxContentsFee" => record.EstimatedBoxContentsFee = BoxContentsFeeDetails::from_xml(s).map(Some)?,
        _ => {},
      }
      Ok(())
    })
  }
}

/// Item information for an inbound shipment. Submitted with a call to the CreateInboundShipment or UpdateInboundShipment operation.
#[allow(non_snake_case)]
#[derive(Debug, Default, PartialEq)]
pub struct InboundShipmentItem {
  pub ShipmentId: String,
  pub SellerSKU: String,
  pub FulfillmentNetworkSKU: String,
  pub QuantityShipped: i32,
  pub QuantityReceived: Option<i32>,
  pub QuantityInCase: Option<i32>,
  // PrepDetailsList: List of PrepDetails
  // ReleaseDate: xs:string,
}

impl<S: decode::XmlEventStream> decode::FromXMLStream<S> for InboundShipmentItem {
  fn from_xml(s: &mut S) -> decode::Result<InboundShipmentItem> {
    use xmlhelper::decode::{fold_elements, characters};
    fold_elements(s, InboundShipmentItem::default(), |s, record| {
      match s.local_name() {
        "ShipmentId" => record.ShipmentId = characters(s)?,
        "SellerSKU" => record.SellerSKU = characters(s)?,
        "FulfillmentNetworkSKU" => record.FulfillmentNetworkSKU = characters(s)?,
        "QuantityShipped" => record.QuantityShipped = characters(s)?,
        "QuantityReceived" => record.QuantityReceived = characters(s).map(Some)?,
        "QuantityInCase" => record.QuantityInCase = characters(s).map(Some)?,
        _ => {},
      }
      Ok(())
    })
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  use xmlhelper::decode;
  use xmlhelper::decode::FromXMLStream;
  use std::io::Cursor;

  #[test]
  fn test_decode_inbound_shipment_info() {
    let mut s = decode::Stream::new(Cursor::new(r#"
    <member>
        <ShipFromAddress>
            <PostalCode>V5V 1A1</PostalCode>
            <Name>Janani Arvind FBA QA</Name>
            <CountryCode>CA</CountryCode>
            <StateOrProvinceCode>BC</StateOrProvinceCode>
            <AddressLine1>Address 1</AddressLine1>
            <City>Vancouver</City>
        </ShipFromAddress>
        <ShipmentId>FBA1123</ShipmentId>
        <ShipmentName>Test MWS CA Shipment 1</ShipmentName>
        <ShipmentStatus>WORKING</ShipmentStatus>
        <LabelPrepType>NO_LABEL</LabelPrepType>
        <DestinationFulfillmentCenterId>RIC2</DestinationFulfillmentCenterId>
        <BoxContentsSource>NONE</BoxContentsSource>
        <EstimatedBoxContentsFee>
            <TotalUnits>10</TotalUnits>
            <FeePerUnit>
                <CurrencyCode>USD</CurrencyCode>
                <Value>0.10</Value>
            </FeePerUnit>
            <TotalFee>
                <CurrencyCode>USD</CurrencyCode>
                <Value>10.0</Value>
            </TotalFee>
        </EstimatedBoxContentsFee>
    </member>
    "#));

    decode::start_document(&mut s).expect("start element");
    let record = decode::element(&mut s, "member", |s| InboundShipmentInfo::from_xml(s)).unwrap();
    assert_eq!(record, InboundShipmentInfo {
      ShipFromAddress: Address {
        PostalCode: "V5V 1A1".to_owned(),
        Name: "Janani Arvind FBA QA".to_owned(),
        CountryCode: "CA".to_owned(),
        StateOrProvinceCode: "BC".to_owned(),
        AddressLine1: "Address 1".to_owned(),
        AddressLine2: "".to_owned(),
        City: "Vancouver".to_owned(),
        DistrictOrCounty: "".to_owned(),
      },
      ShipmentId: "FBA1123".to_owned(),
      ShipmentName: "Test MWS CA Shipment 1".to_owned(),
      ShipmentStatus: ShipmentStatus::WORKING,
      LabelPrepType: Some(LabelPrepType::NO_LABEL),
      DestinationFulfillmentCenterId: "RIC2".to_owned(),
      BoxContentsSource: Some(BoxContentsSource::NONE),
      EstimatedBoxContentsFee: Some(BoxContentsFeeDetails {
        TotalUnits: 10,
        FeePerUnit: Some(Amount {
          CurrencyCode: "USD".to_owned(),
          Value: 0.10,
        }),
        TotalFee: Some(Amount {
          CurrencyCode: "USD".to_owned(),
          Value: 10.0,
        })
      }),
      AreCasesRequired: false,
      ConfirmedNeedByDate: "".to_owned(),
    });
  }

  #[test]
  fn test_decode_inbound_shipment_item() {
    let mut s = decode::Stream::new(Cursor::new(r#"
    <member>
        <ShipmentId>SSF85DGIZZ3OF1</ShipmentId>
        <SellerSKU>SampleSKU2</SellerSKU>
        <QuantityShipped>10</QuantityShipped>
        <QuantityInCase>0</QuantityInCase>
        <QuantityReceived>0</QuantityReceived>
        <FulfillmentNetworkSKU>B0011VECH4</FulfillmentNetworkSKU>
    </member>
    "#));

    decode::start_document(&mut s).expect("start element");
    let record = decode::element(&mut s, "member", |s| InboundShipmentItem::from_xml(s)).unwrap();

    assert_eq!(record, InboundShipmentItem {
      ShipmentId: "SSF85DGIZZ3OF1".to_owned(),
      SellerSKU: "SampleSKU2".to_owned(),
      QuantityShipped: 10,
      QuantityInCase: Some(0),
      QuantityReceived: Some(0),
      FulfillmentNetworkSKU: "B0011VECH4".to_owned(),
    });
  }
}<commit_msg>make InboundShipmentInfo.ConfirmedNeedByDate optional<commit_after>use xmlhelper::decode;

str_enum! {
  pub enum ShipmentStatus  {
    WORKING,    // - The shipment was created by the seller, but has not yet shipped.
    SHIPPED,    // - The shipment was picked up by the carrier.
    IN_TRANSIT, // - The carrier has notified the Amazon fulfillment center that it is aware of the shipment.
    DELIVERED,  // - The shipment was delivered by the carrier to the Amazon fulfillment center.
    CHECKED_IN, // - The shipment was checked-in at the receiving dock of the Amazon fulfillment center.
    RECEIVING,  // - The shipment has arrived at the Amazon fulfillment center, but not all items have been marked as received.
    CLOSED,     // - The shipment has arrived at the Amazon fulfillment center and all items have been marked as received.
    CANCELLED,  // - The shipment was cancelled by the seller after the shipment was sent to the Amazon fulfillment center.
    DELETED,    // - The shipment was cancelled by the seller before the shipment was sent to the Amazon fulfillment center.
    ERROR,      // - There was an error with the shipment and it was not processed by Amazon.
  }
}

/// Where the seller provided box contents information for a shipment. This is only returned for shipments to US fulfillment centers.
string_map_enum! {
  pub enum BoxContentsSource {
    NONE = "NONE",
    FEED = "FEED",
    _2D_BARCODE = "2D_BARCODE",
    INTERACTIVE = "INTERACTIVE",
  }
}

#[allow(non_snake_case)]
#[derive(Debug, Default, PartialEq)]
pub struct Amount {
  pub CurrencyCode: String,
  pub Value: f64,
}

impl<S: decode::XmlEventStream> decode::FromXMLStream<S> for Amount {
  fn from_xml(s: &mut S) -> decode::Result<Amount> {
    use xmlhelper::decode::{fold_elements, characters};
    fold_elements(s, Amount::default(), |s, record| {
      match s.local_name() {
        "CurrencyCode" => record.CurrencyCode = characters(s)?,
        "Value" => record.Value = characters(s)?,
        _ => {},
      }
      Ok(())
    })
  }
}

/// The manual processing fee per unit and total fee for a shipment.
#[allow(non_snake_case)]
#[derive(Debug, Default, PartialEq)]
pub struct BoxContentsFeeDetails {
  pub TotalUnits: i32,
  pub FeePerUnit: Option<Amount>,
  pub TotalFee: Option<Amount>,
}

impl<S: decode::XmlEventStream> decode::FromXMLStream<S> for BoxContentsFeeDetails {
  fn from_xml(s: &mut S) -> decode::Result<BoxContentsFeeDetails> {
    use xmlhelper::decode::{fold_elements, characters};
    fold_elements(s, BoxContentsFeeDetails::default(), |s, record| {
      match s.local_name() {
        "TotalUnits" => record.TotalUnits = characters(s)?,
        "FeePerUnit" => record.FeePerUnit = Amount::from_xml(s).map(Some)?,
        "TotalFee" => record.TotalFee = Amount::from_xml(s).map(Some)?,
        _ => {},
      }
      Ok(())
    })
  }
}

str_enum! {
  pub enum LabelPrepType {
    NO_LABEL,
    SELLER_LABEL,
    AMAZON_LABEL,
  }
}

/// Postal address information.
#[allow(non_snake_case)]
#[derive(Debug, Default, PartialEq)]
pub struct Address {
  /// The name or business name.
  pub Name: String,
  /// The street address information.
  pub AddressLine1: String,
  ///	Additional street address information, if required.
  pub AddressLine2: String,
  /// The city.
  pub City: String,
  /// The district or county.
  pub DistrictOrCounty: String,
  /// The state or province code.
  pub StateOrProvinceCode: String,
  /// The country code.
  pub CountryCode: String,
  /// The postal code.
  pub PostalCode: String,
}

impl<S: decode::XmlEventStream> decode::FromXMLStream<S> for Address {
  fn from_xml(s: &mut S) -> decode::Result<Address> {
    use xmlhelper::decode::{fold_elements, characters};
    fold_elements(s, Address::default(), |s, record| {
      match s.local_name() {
        "Name" => record.Name = characters(s)?,
        "AddressLine1" => record.AddressLine1 = characters(s)?,
        "AddressLine2" => record.AddressLine2 = characters(s)?,
        "City" => record.City = characters(s)?,
        "DistrictOrCounty" => record.DistrictOrCounty = characters(s)?,
        "StateOrProvinceCode" => record.StateOrProvinceCode = characters(s)?,
        "CountryCode" => record.CountryCode = characters(s)?,
        "PostalCode" => record.PostalCode = characters(s)?,
        _ => {},
      }
      Ok(())
    })
  }
}

/// Information about your inbound shipments. Returned by the `ListInboundShipments` operation.
#[allow(non_snake_case)]
#[derive(Debug, Default, PartialEq)]
pub struct InboundShipmentInfo {
  /// The ShipmentId submitted in the request.
  pub ShipmentId: String,
  /// A unique name that you provide for your inbound shipment.
  pub ShipmentName: String,
  /// Your return address.
  pub ShipFromAddress: Address,
  /// An Amazon fulfillment center identifier created by Amazon.
  pub DestinationFulfillmentCenterId: String,
  /// The type of label preparation that is required for your inbound shipment.
  pub LabelPrepType: Option<LabelPrepType>,
  /// The status of your inbound shipment.
  pub ShipmentStatus: ShipmentStatus,
  /// Indicates whether or not an inbound shipment contains case-packed boxes.
  /// When AreCasesRequired = true for an inbound shipment, all items in the
  /// inbound shipment must be case packed.
  pub AreCasesRequired: bool,
  ///	Date that the shipment must arrive at an Amazon fulfillment center to avoid
  /// delivery promise breaks for pre-ordered items. For more information, see
  /// GetPreorderInfo. Pre-orders are only available in India and Japan.
  pub ConfirmedNeedByDate: Option<String>,
  ///	Where the seller provided box contents information for a shipment. This is
  /// only returned for shipments to US fulfillment centers.
  pub BoxContentsSource: Option<BoxContentsSource>,
  /// An estimate of the manual processing fee charged by Amazon for boxes
  /// without box content information. This is only returned when BoxContentsSource is NONE.
  pub EstimatedBoxContentsFee: Option<BoxContentsFeeDetails>,
}

impl<S: decode::XmlEventStream> decode::FromXMLStream<S> for InboundShipmentInfo {
  fn from_xml(s: &mut S) -> decode::Result<InboundShipmentInfo> {
    use xmlhelper::decode::{fold_elements, characters};
    fold_elements(s, InboundShipmentInfo::default(), |s, record| {
      match s.local_name() {
        "ShipmentId" => record.ShipmentId = characters(s)?,
        "ShipmentName" => record.ShipmentName = characters(s)?,
        "ShipFromAddress" => record.ShipFromAddress = Address::from_xml(s)?,
        "DestinationFulfillmentCenterId" => record.DestinationFulfillmentCenterId = characters(s)?,
        "LabelPrepType" => record.LabelPrepType = characters(s).map(Some)?,
        "ShipmentStatus" => record.ShipmentStatus = characters(s)?,
        "AreCasesRequired" => record.AreCasesRequired = characters(s)?,
        "ConfirmedNeedByDate" => record.ConfirmedNeedByDate = characters(s).map(Some)?,
        "BoxContentsSource" => record.BoxContentsSource = characters(s).map(Some)?,
        "EstimatedBoxContentsFee" => record.EstimatedBoxContentsFee = BoxContentsFeeDetails::from_xml(s).map(Some)?,
        _ => {},
      }
      Ok(())
    })
  }
}

/// Item information for an inbound shipment. Submitted with a call to the CreateInboundShipment or UpdateInboundShipment operation.
#[allow(non_snake_case)]
#[derive(Debug, Default, PartialEq)]
pub struct InboundShipmentItem {
  pub ShipmentId: String,
  pub SellerSKU: String,
  pub FulfillmentNetworkSKU: String,
  pub QuantityShipped: i32,
  pub QuantityReceived: Option<i32>,
  pub QuantityInCase: Option<i32>,
  // PrepDetailsList: List of PrepDetails
  // ReleaseDate: xs:string,
}

impl<S: decode::XmlEventStream> decode::FromXMLStream<S> for InboundShipmentItem {
  fn from_xml(s: &mut S) -> decode::Result<InboundShipmentItem> {
    use xmlhelper::decode::{fold_elements, characters};
    fold_elements(s, InboundShipmentItem::default(), |s, record| {
      match s.local_name() {
        "ShipmentId" => record.ShipmentId = characters(s)?,
        "SellerSKU" => record.SellerSKU = characters(s)?,
        "FulfillmentNetworkSKU" => record.FulfillmentNetworkSKU = characters(s)?,
        "QuantityShipped" => record.QuantityShipped = characters(s)?,
        "QuantityReceived" => record.QuantityReceived = characters(s).map(Some)?,
        "QuantityInCase" => record.QuantityInCase = characters(s).map(Some)?,
        _ => {},
      }
      Ok(())
    })
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  use xmlhelper::decode;
  use xmlhelper::decode::FromXMLStream;
  use std::io::Cursor;

  #[test]
  fn test_decode_inbound_shipment_info() {
    let mut s = decode::Stream::new(Cursor::new(r#"
    <member>
        <ShipFromAddress>
            <PostalCode>V5V 1A1</PostalCode>
            <Name>Janani Arvind FBA QA</Name>
            <CountryCode>CA</CountryCode>
            <StateOrProvinceCode>BC</StateOrProvinceCode>
            <AddressLine1>Address 1</AddressLine1>
            <City>Vancouver</City>
        </ShipFromAddress>
        <ShipmentId>FBA1123</ShipmentId>
        <ShipmentName>Test MWS CA Shipment 1</ShipmentName>
        <ShipmentStatus>WORKING</ShipmentStatus>
        <LabelPrepType>NO_LABEL</LabelPrepType>
        <DestinationFulfillmentCenterId>RIC2</DestinationFulfillmentCenterId>
        <BoxContentsSource>NONE</BoxContentsSource>
        <EstimatedBoxContentsFee>
            <TotalUnits>10</TotalUnits>
            <FeePerUnit>
                <CurrencyCode>USD</CurrencyCode>
                <Value>0.10</Value>
            </FeePerUnit>
            <TotalFee>
                <CurrencyCode>USD</CurrencyCode>
                <Value>10.0</Value>
            </TotalFee>
        </EstimatedBoxContentsFee>
    </member>
    "#));

    decode::start_document(&mut s).expect("start element");
    let record = decode::element(&mut s, "member", |s| InboundShipmentInfo::from_xml(s)).unwrap();
    assert_eq!(record, InboundShipmentInfo {
      ShipFromAddress: Address {
        PostalCode: "V5V 1A1".to_owned(),
        Name: "Janani Arvind FBA QA".to_owned(),
        CountryCode: "CA".to_owned(),
        StateOrProvinceCode: "BC".to_owned(),
        AddressLine1: "Address 1".to_owned(),
        AddressLine2: "".to_owned(),
        City: "Vancouver".to_owned(),
        DistrictOrCounty: "".to_owned(),
      },
      ShipmentId: "FBA1123".to_owned(),
      ShipmentName: "Test MWS CA Shipment 1".to_owned(),
      ShipmentStatus: ShipmentStatus::WORKING,
      LabelPrepType: Some(LabelPrepType::NO_LABEL),
      DestinationFulfillmentCenterId: "RIC2".to_owned(),
      BoxContentsSource: Some(BoxContentsSource::NONE),
      EstimatedBoxContentsFee: Some(BoxContentsFeeDetails {
        TotalUnits: 10,
        FeePerUnit: Some(Amount {
          CurrencyCode: "USD".to_owned(),
          Value: 0.10,
        }),
        TotalFee: Some(Amount {
          CurrencyCode: "USD".to_owned(),
          Value: 10.0,
        })
      }),
      AreCasesRequired: false,
      ConfirmedNeedByDate: "".to_owned(),
    });
  }

  #[test]
  fn test_decode_inbound_shipment_item() {
    let mut s = decode::Stream::new(Cursor::new(r#"
    <member>
        <ShipmentId>SSF85DGIZZ3OF1</ShipmentId>
        <SellerSKU>SampleSKU2</SellerSKU>
        <QuantityShipped>10</QuantityShipped>
        <QuantityInCase>0</QuantityInCase>
        <QuantityReceived>0</QuantityReceived>
        <FulfillmentNetworkSKU>B0011VECH4</FulfillmentNetworkSKU>
    </member>
    "#));

    decode::start_document(&mut s).expect("start element");
    let record = decode::element(&mut s, "member", |s| InboundShipmentItem::from_xml(s)).unwrap();

    assert_eq!(record, InboundShipmentItem {
      ShipmentId: "SSF85DGIZZ3OF1".to_owned(),
      SellerSKU: "SampleSKU2".to_owned(),
      QuantityShipped: 10,
      QuantityInCase: Some(0),
      QuantityReceived: Some(0),
      FulfillmentNetworkSKU: "B0011VECH4".to_owned(),
    });
  }
}<|endoftext|>


<commit_before>// Copyright 2013-2014 The gl-rs developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// 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.

#![experimental]

use registry::{Registry, Ns};
use syntax::ast;
use syntax::ext::base::ExtCtxt;
use syntax::ext::quote::rt::ExtParseUtils;
use syntax::ptr::P;

pub struct StructGenerator;

impl super::Generator for StructGenerator {
    fn write(&self, ecx: &ExtCtxt, registry: &Registry, ns: Ns) -> Vec<P<ast::Item>> {
        let mut result = Vec::new();
        result.push(write_header(ecx));
        result.push(write_type_aliases(ecx, &ns));
        result.extend(write_enums(ecx, registry).into_iter());
        result.extend(write_fnptr_struct_def(ecx).into_iter());
        result.push(write_failing_fns(ecx, registry));
        result.push(write_struct(ecx, registry, &ns));
        result.push(write_impl(ecx, registry, &ns));
        result
    }
}

/// Creates a `__gl_imports` module which contains all the external symbols that we need for the
///  bindings.
fn write_header(ecx: &ExtCtxt) -> P<ast::Item> {
    (quote_item!(ecx,
        mod __gl_imports {
            extern crate gl_common;
            extern crate libc;
            pub use std::mem;
        }
    )).unwrap()
}

/// Creates a `types` module which contains all the type aliases.
///
/// See also `generators::gen_type_aliases`.
fn write_type_aliases(ecx: &ExtCtxt, ns: &Ns) -> P<ast::Item> {
    let aliases = super::gen_type_aliases(ecx, ns);

    (quote_item!(ecx,
        #[stable]
        pub mod types {
            #![allow(non_camel_case_types)]
            #![allow(non_snake_case)]
            #![allow(dead_code)]

            $aliases
        }
    )).unwrap()
}

/// Writes all the `<enum>` elements at the root of the bindings.
fn write_enums(ecx: &ExtCtxt, registry: &Registry) -> Vec<P<ast::Item>> {
    registry.enum_iter().map(|e| {
        super::gen_enum_item(ecx, e, "types::")
    }).collect()
}

/// Creates a `FnPtr` structure which contains the store for a single binding.
fn write_fnptr_struct_def(ecx: &ExtCtxt) -> Vec<P<ast::Item>> {
    let mut result = Vec::new();

    result.push((quote_item!(ecx,
        #[allow(dead_code)]
        pub struct FnPtr {
            /// The function pointer that will be used when calling the function.
            f: *const __gl_imports::libc::c_void,
            /// True if the pointer points to a real function, false if points to a `panic!` fn.
            is_loaded: bool,
        }
    )).unwrap());

    result.push((quote_item!(ecx,
        impl FnPtr {
            /// Creates a `FnPtr` from a load attempt.
            fn new(ptr: *const __gl_imports::libc::c_void,
                failing_fn: *const __gl_imports::libc::c_void) -> FnPtr {
                if ptr.is_null() {
                    FnPtr { f: failing_fn, is_loaded: false }
                } else {
                    FnPtr { f: ptr, is_loaded: true }
                }
            }

            /// Returns `true` if the function has been successfully loaded.
            ///
            /// If it returns `false`, calling the corresponding function will fail.
            #[inline]
            #[allow(dead_code)]
            pub fn is_loaded(&self) -> bool {
                self.is_loaded
            }
        }
    )).unwrap());

    result
}

/// Creates a `failing` module which contains one function per GL command.
///
/// These functions are the mocks that are called if the real function could not be loaded.
fn write_failing_fns(ecx: &ExtCtxt, registry: &Registry) -> P<ast::Item> {
    let fns = registry.cmd_iter().map(|c| {
        use syntax::ext::quote::rt::ToSource;

        ecx.parse_item(format!(r#"
            #[allow(unused_variables)]
            #[allow(non_snake_case)]
            #[allow(dead_code)]
            pub extern "system" fn {name}({params}) -> {return_suffix} {{
                panic!("`{name}` was not loaded")
            }}
            "#,
            name = c.proto.ident,
            params = super::gen_parameters(ecx, c).into_iter().map(|p| p.to_source()).collect::<Vec<String>>().connect(", "),
            return_suffix = super::gen_return_type(ecx, c).to_source()
        ))
    }).collect::<Vec<P<ast::Item>>>();

    (quote_item!(ecx,
        mod failing {
            use super::types;
            use super::__gl_imports;

            $fns
        }
    )).unwrap()
}

/// Creates a structure which stores all the `FnPtr` of the bindings.
///
/// The name of the struct corresponds to the namespace.
fn write_struct(ecx: &ExtCtxt, registry: &Registry, ns: &Ns) -> P<ast::Item> {
    ecx.parse_item(format!("
        #[allow(non_camel_case_types)]
        #[allow(non_snake_case)]
        #[allow(dead_code)]
        #[stable]
        pub struct {ns:c} {{
            {ptrs}
        }}",

        ns = *ns,
        ptrs = registry.cmd_iter().map(|c| {
            let fallbacks = match registry.aliases.get(&c.proto.ident) {
                Some(v) => v.clone(),
                None => Vec::new(),
            };
            format!(
                "{doc} pub {name}: FnPtr,",
                doc = if fallbacks.len() == 0 { "".to_string() } else { format!("/** Fallbacks: {} */", fallbacks.connect(", ")) },
                name = c.proto.ident,
            )
        }).collect::<Vec<String>>().connect("\n")
    ))
}

/// Creates the `impl` of the structure created by `write_struct`.
fn write_impl(ecx: &ExtCtxt, registry: &Registry, ns: &Ns) -> P<ast::Item> {
    ecx.parse_item(format!("
        impl {ns:c} {{
            /// Load each OpenGL symbol using a custom load function. This allows for the
            /// use of functions like `glfwGetProcAddress` or `SDL_GL_GetProcAddress`.
            ///
            /// ~~~ignore
            /// let gl = Gl::load_with(|s| glfw.get_proc_address(s));
            /// ~~~
            #[unstable]
            #[allow(dead_code)]
            #[allow(unused_variables)]
            pub fn load_with(loadfn: |symbol: &str| -> *const __gl_imports::libc::c_void) -> {ns:c} {{
                let metaloadfn: |&str, &[&str]| -> *const __gl_imports::libc::c_void = |symbol, symbols| {{
                    let mut ptr = loadfn(symbol);
                    if ptr.is_null() {{
                        for &sym in symbols.iter() {{
                            ptr = loadfn(sym);
                            if !ptr.is_null() {{ break; }}
                        }}
                    }}
                    ptr
                }};
                {ns:c} {{
                    {loadings}
                }}
            }}

            /// Load each OpenGL symbol using a custom load function.
            ///
            /// ~~~ignore
            /// let gl = Gl::load(&glfw);
            /// ~~~
            #[unstable]
            #[allow(dead_code)]
            #[allow(unused_variables)]
            pub fn load<T: __gl_imports::gl_common::GlFunctionsSource>(loader: &T) -> {ns:c} {{
                {ns:c}::load_with(|name| loader.get_proc_addr(name))
            }}

            {modules}
        }}",

        ns = *ns,

        loadings = registry.cmd_iter().map(|c| {
            let fallbacks = registry.aliases.get(&c.proto.ident);
            format!(
                "{name}: FnPtr::new(metaloadfn(\"{symbol}\", {fb}), failing::{name} as *const __gl_imports::libc::c_void),",
                name = c.proto.ident,
                symbol = super::gen_symbol_name(ns, c.proto.ident.as_slice()),
                fb = match fallbacks {
                    Some(fallbacks) => format!("[{}]", fallbacks.iter().map(|name| format!("\"{}\"", name)).collect::<Vec<_>>().connect(", ")),
                    None => format!("[]")
                },
            )
        }).collect::<Vec<String>>().connect("\n"),

        modules = registry.cmd_iter().map(|c| {
            use syntax::ext::quote::rt::ToSource;

            format!(
                "#[allow(non_snake_case)] #[allow(unused_variables)] #[allow(dead_code)]
                #[inline] #[unstable] pub unsafe fn {name}(&self, {typed_params}) -> {return_suffix} {{ \
                    __gl_imports::mem::transmute::<_, extern \"system\" fn({typed_params}) -> {return_suffix}>\
                        (self.{name}.f)({idents}) \
                }}",
                name = c.proto.ident,
                typed_params = super::gen_parameters(ecx, c).into_iter().map(|p| p.to_source()).collect::<Vec<String>>().connect(", "),
                return_suffix = super::gen_return_type(ecx, c).to_source(),
                idents = super::gen_parameters(ecx, c).into_iter().map(|p| p.pat.to_source()).collect::<Vec<String>>().connect(", "),
            )
        }).collect::<Vec<String>>().connect("\n")
    ))
}
<commit_msg>Update for rustc<commit_after>// Copyright 2013-2014 The gl-rs developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// 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.

#![experimental]

use registry::{Registry, Ns};
use syntax::ast;
use syntax::ext::base::ExtCtxt;
use syntax::ext::quote::rt::ExtParseUtils;
use syntax::ptr::P;

pub struct StructGenerator;

impl super::Generator for StructGenerator {
    fn write(&self, ecx: &ExtCtxt, registry: &Registry, ns: Ns) -> Vec<P<ast::Item>> {
        let mut result = Vec::new();
        result.push(write_header(ecx));
        result.push(write_type_aliases(ecx, &ns));
        result.extend(write_enums(ecx, registry).into_iter());
        result.extend(write_fnptr_struct_def(ecx).into_iter());
        result.push(write_failing_fns(ecx, registry));
        result.push(write_struct(ecx, registry, &ns));
        result.push(write_impl(ecx, registry, &ns));
        result
    }
}

/// Creates a `__gl_imports` module which contains all the external symbols that we need for the
///  bindings.
fn write_header(ecx: &ExtCtxt) -> P<ast::Item> {
    (quote_item!(ecx,
        mod __gl_imports {
            extern crate gl_common;
            extern crate libc;
            pub use std::mem;
        }
    )).unwrap()
}

/// Creates a `types` module which contains all the type aliases.
///
/// See also `generators::gen_type_aliases`.
fn write_type_aliases(ecx: &ExtCtxt, ns: &Ns) -> P<ast::Item> {
    let aliases = super::gen_type_aliases(ecx, ns);

    (quote_item!(ecx,
        #[stable]
        pub mod types {
            #![allow(non_camel_case_types)]
            #![allow(non_snake_case)]
            #![allow(dead_code)]

            $aliases
        }
    )).unwrap()
}

/// Writes all the `<enum>` elements at the root of the bindings.
fn write_enums(ecx: &ExtCtxt, registry: &Registry) -> Vec<P<ast::Item>> {
    registry.enum_iter().map(|e| {
        super::gen_enum_item(ecx, e, "types::")
    }).collect()
}

/// Creates a `FnPtr` structure which contains the store for a single binding.
fn write_fnptr_struct_def(ecx: &ExtCtxt) -> Vec<P<ast::Item>> {
    let mut result = Vec::new();

    result.push((quote_item!(ecx,
        #[allow(dead_code)]
        pub struct FnPtr {
            /// The function pointer that will be used when calling the function.
            f: *const __gl_imports::libc::c_void,
            /// True if the pointer points to a real function, false if points to a `panic!` fn.
            is_loaded: bool,
        }
    )).unwrap());

    result.push((quote_item!(ecx,
        impl FnPtr {
            /// Creates a `FnPtr` from a load attempt.
            fn new(ptr: *const __gl_imports::libc::c_void,
                failing_fn: *const __gl_imports::libc::c_void) -> FnPtr {
                if ptr.is_null() {
                    FnPtr { f: failing_fn, is_loaded: false }
                } else {
                    FnPtr { f: ptr, is_loaded: true }
                }
            }

            /// Returns `true` if the function has been successfully loaded.
            ///
            /// If it returns `false`, calling the corresponding function will fail.
            #[inline]
            #[allow(dead_code)]
            pub fn is_loaded(&self) -> bool {
                self.is_loaded
            }
        }
    )).unwrap());

    result
}

/// Creates a `failing` module which contains one function per GL command.
///
/// These functions are the mocks that are called if the real function could not be loaded.
fn write_failing_fns(ecx: &ExtCtxt, registry: &Registry) -> P<ast::Item> {
    let fns = registry.cmd_iter().map(|c| {
        use syntax::ext::quote::rt::ToSource;

        ecx.parse_item(format!(r#"
            #[allow(unused_variables)]
            #[allow(non_snake_case)]
            #[allow(dead_code)]
            pub extern "system" fn {name}({params}) -> {return_suffix} {{
                panic!("`{name}` was not loaded")
            }}
            "#,
            name = c.proto.ident,
            params = super::gen_parameters(ecx, c).into_iter().map(|p| p.to_source()).collect::<Vec<String>>().connect(", "),
            return_suffix = super::gen_return_type(ecx, c).to_source()
        ))
    }).collect::<Vec<P<ast::Item>>>();

    (quote_item!(ecx,
        mod failing {
            use super::types;
            use super::__gl_imports;

            $fns
        }
    )).unwrap()
}

/// Creates a structure which stores all the `FnPtr` of the bindings.
///
/// The name of the struct corresponds to the namespace.
fn write_struct(ecx: &ExtCtxt, registry: &Registry, ns: &Ns) -> P<ast::Item> {
    ecx.parse_item(format!("
        #[allow(non_camel_case_types)]
        #[allow(non_snake_case)]
        #[allow(dead_code)]
        #[stable]
        pub struct {ns:c} {{
            {ptrs}
        }}",

        ns = *ns,
        ptrs = registry.cmd_iter().map(|c| {
            let fallbacks = match registry.aliases.get(&c.proto.ident) {
                Some(v) => v.clone(),
                None => Vec::new(),
            };
            format!(
                "{doc} pub {name}: FnPtr,",
                doc = if fallbacks.len() == 0 { "".to_string() } else { format!("/** Fallbacks: {} */", fallbacks.connect(", ")) },
                name = c.proto.ident,
            )
        }).collect::<Vec<String>>().connect("\n")
    ))
}

/// Creates the `impl` of the structure created by `write_struct`.
fn write_impl(ecx: &ExtCtxt, registry: &Registry, ns: &Ns) -> P<ast::Item> {
    ecx.parse_item(format!("
        impl {ns:c} {{
            /// Load each OpenGL symbol using a custom load function. This allows for the
            /// use of functions like `glfwGetProcAddress` or `SDL_GL_GetProcAddress`.
            ///
            /// ~~~ignore
            /// let gl = Gl::load_with(|s| glfw.get_proc_address(s));
            /// ~~~
            #[unstable]
            #[allow(dead_code)]
            #[allow(unused_variables)]
            pub fn load_with(loadfn: |symbol: &str| -> *const __gl_imports::libc::c_void) -> {ns:c} {{
                let metaloadfn: |&str, &[&str]| -> *const __gl_imports::libc::c_void = |symbol, symbols| {{
                    let mut ptr = loadfn(symbol);
                    if ptr.is_null() {{
                        for &sym in symbols.iter() {{
                            ptr = loadfn(sym);
                            if !ptr.is_null() {{ break; }}
                        }}
                    }}
                    ptr
                }};
                {ns:c} {{
                    {loadings}
                }}
            }}

            /// Load each OpenGL symbol using a custom load function.
            ///
            /// ~~~ignore
            /// let gl = Gl::load(&glfw);
            /// ~~~
            #[unstable]
            #[allow(dead_code)]
            #[allow(unused_variables)]
            pub fn load<T: __gl_imports::gl_common::GlFunctionsSource>(loader: &T) -> {ns:c} {{
                {ns:c}::load_with(|name| loader.get_proc_addr(name))
            }}

            {modules}
        }}",

        ns = *ns,

        loadings = registry.cmd_iter().map(|c| {
            let fallbacks = registry.aliases.get(&c.proto.ident);
            format!(
                "{name}: FnPtr::new(metaloadfn(\"{symbol}\", {fb}), failing::{name} as *const __gl_imports::libc::c_void),",
                name = c.proto.ident,
                symbol = super::gen_symbol_name(ns, c.proto.ident.as_slice()),
                fb = match fallbacks {
                    Some(fallbacks) => format!("&[{}]", fallbacks.iter().map(|name| format!("\"{}\"", name)).collect::<Vec<_>>().connect(", ")),
                    None => format!("&[]")
                },
            )
        }).collect::<Vec<String>>().connect("\n"),

        modules = registry.cmd_iter().map(|c| {
            use syntax::ext::quote::rt::ToSource;

            format!(
                "#[allow(non_snake_case)] #[allow(unused_variables)] #[allow(dead_code)]
                #[inline] #[unstable] pub unsafe fn {name}(&self, {typed_params}) -> {return_suffix} {{ \
                    __gl_imports::mem::transmute::<_, extern \"system\" fn({typed_params}) -> {return_suffix}>\
                        (self.{name}.f)({idents}) \
                }}",
                name = c.proto.ident,
                typed_params = super::gen_parameters(ecx, c).into_iter().map(|p| p.to_source()).collect::<Vec<String>>().connect(", "),
                return_suffix = super::gen_return_type(ecx, c).to_source(),
                idents = super::gen_parameters(ecx, c).into_iter().map(|p| p.pat.to_source()).collect::<Vec<String>>().connect(", "),
            )
        }).collect::<Vec<String>>().connect("\n")
    ))
}
<|endoftext|>


<commit_before>use syntax::{ast, fold, attr};

export strip_unconfigured_items;
export metas_in_cfg;
export strip_items;

type in_cfg_pred = fn@(~[ast::attribute]) -> bool;

type ctxt = @{
    in_cfg: in_cfg_pred
};

// Support conditional compilation by transforming the AST, stripping out
// any items that do not belong in the current configuration
fn strip_unconfigured_items(crate: @ast::crate) -> @ast::crate {
    do strip_items(crate) |attrs| {
        in_cfg(crate.node.config, attrs)
    }
}

fn strip_items(crate: @ast::crate, in_cfg: in_cfg_pred)
    -> @ast::crate {

    let ctxt = @{in_cfg: in_cfg};

    let precursor =
        @{fold_mod: |a,b| fold_mod(ctxt, a, b),
          fold_block: fold::wrap(|a,b| fold_block(ctxt, a, b) ),
          fold_foreign_mod: |a,b| fold_foreign_mod(ctxt, a, b),
          fold_item_underscore: |a,b| fold_item_underscore(ctxt, a, b),
          .. *fold::default_ast_fold()};

    let fold = fold::make_fold(precursor);
    let res = @fold.fold_crate(*crate);
    return res;
}

fn filter_item(cx: ctxt, &&item: @ast::item) ->
   Option<@ast::item> {
    if item_in_cfg(cx, item) { option::Some(item) } else { option::None }
}

fn filter_view_item(cx: ctxt, &&view_item: @ast::view_item
                   )-> Option<@ast::view_item> {
    if view_item_in_cfg(cx, view_item) {
        option::Some(view_item)
    } else {
        option::None
    }
}

fn fold_mod(cx: ctxt, m: ast::_mod, fld: fold::ast_fold) ->
   ast::_mod {
    let filtered_items = vec::filter_map(m.items, |a| filter_item(cx, *a));
    let filtered_view_items = vec::filter_map(m.view_items,
                                              |a| filter_view_item(cx, *a));
    return {
        view_items: vec::map(filtered_view_items, |x| fld.fold_view_item(*x)),
        items: vec::filter_map(filtered_items, |x| fld.fold_item(*x))
    };
}

fn filter_foreign_item(cx: ctxt, &&item: @ast::foreign_item) ->
   Option<@ast::foreign_item> {
    if foreign_item_in_cfg(cx, item) {
        option::Some(item)
    } else { option::None }
}

fn fold_foreign_mod(cx: ctxt, nm: ast::foreign_mod,
                   fld: fold::ast_fold) -> ast::foreign_mod {
    let filtered_items = vec::filter_map(nm.items,
                                         |a| filter_foreign_item(cx, *a));
    let filtered_view_items = vec::filter_map(nm.view_items,
                                              |a| filter_view_item(cx, *a));
    return {
        sort: nm.sort,
        view_items: vec::map(filtered_view_items, |x| fld.fold_view_item(*x)),
        items: filtered_items
    };
}

fn fold_item_underscore(cx: ctxt, item: ast::item_, fld: fold::ast_fold) -> ast::item_ {
    let item = match item {
        ast::item_impl(a, b, c, Some(methods)) => {
            let methods = methods.filter(|m| method_in_cfg(cx, *m) );
            ast::item_impl(a, b, c, Some(methods))
        }
        ast::item_trait(a, b, ref methods) => {
            let methods = methods.filter(|m| trait_method_in_cfg(cx, m) );
            ast::item_trait(a, b, methods)
        }
        _ => item
    };

    fold::noop_fold_item_underscore(item, fld)
}

fn filter_stmt(cx: ctxt, &&stmt: @ast::stmt) ->
   Option<@ast::stmt> {
    match stmt.node {
      ast::stmt_decl(decl, _) => {
        match decl.node {
          ast::decl_item(item) => {
            if item_in_cfg(cx, item) {
                option::Some(stmt)
            } else { option::None }
          }
          _ => option::Some(stmt)
        }
      }
      _ => option::Some(stmt)
    }
}

fn fold_block(cx: ctxt, b: ast::blk_, fld: fold::ast_fold) ->
   ast::blk_ {
    let filtered_stmts = vec::filter_map(b.stmts, |a| filter_stmt(cx, *a));
    return {view_items: b.view_items,
         stmts: vec::map(filtered_stmts, |x| fld.fold_stmt(*x)),
         expr: option::map(&b.expr, |x| fld.fold_expr(*x)),
         id: b.id,
         rules: b.rules};
}

fn item_in_cfg(cx: ctxt, item: @ast::item) -> bool {
    return cx.in_cfg(item.attrs);
}

fn foreign_item_in_cfg(cx: ctxt, item: @ast::foreign_item) -> bool {
    return cx.in_cfg(item.attrs);
}

fn view_item_in_cfg(cx: ctxt, item: @ast::view_item) -> bool {
    return cx.in_cfg(item.attrs);
}

fn method_in_cfg(cx: ctxt, meth: @ast::method) -> bool {
    return cx.in_cfg(meth.attrs);
}

fn trait_method_in_cfg(cx: ctxt, meth: &ast::trait_method) -> bool {
    match *meth {
        ast::required(ref meth) => cx.in_cfg(meth.attrs),
        ast::provided(@ref meth) => cx.in_cfg(meth.attrs)
    }
}

// Determine if an item should be translated in the current crate
// configuration based on the item's attributes
fn in_cfg(cfg: ast::crate_cfg, attrs: ~[ast::attribute]) -> bool {
    metas_in_cfg(cfg, attr::attr_metas(attrs))
}

fn metas_in_cfg(cfg: ast::crate_cfg, metas: ~[@ast::meta_item]) -> bool {

    // The "cfg" attributes on the item
    let cfg_metas = attr::find_meta_items_by_name(metas, ~"cfg");

    // Pull the inner meta_items from the #[cfg(meta_item, ...)]  attributes,
    // so we can match against them. This is the list of configurations for
    // which the item is valid
    let cfg_metas = vec::concat(vec::filter_map(cfg_metas,
        |i| attr::get_meta_item_list(*i)));

    let has_cfg_metas = vec::len(cfg_metas) > 0u;
    if !has_cfg_metas { return true; }

    for cfg_metas.each |cfg_mi| {
        if attr::contains(cfg, *cfg_mi) { return true; }
    }

    return false;
}

// Local Variables:
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
<commit_msg>Long lines<commit_after>use syntax::{ast, fold, attr};

export strip_unconfigured_items;
export metas_in_cfg;
export strip_items;

type in_cfg_pred = fn@(~[ast::attribute]) -> bool;

type ctxt = @{
    in_cfg: in_cfg_pred
};

// Support conditional compilation by transforming the AST, stripping out
// any items that do not belong in the current configuration
fn strip_unconfigured_items(crate: @ast::crate) -> @ast::crate {
    do strip_items(crate) |attrs| {
        in_cfg(crate.node.config, attrs)
    }
}

fn strip_items(crate: @ast::crate, in_cfg: in_cfg_pred)
    -> @ast::crate {

    let ctxt = @{in_cfg: in_cfg};

    let precursor =
        @{fold_mod: |a,b| fold_mod(ctxt, a, b),
          fold_block: fold::wrap(|a,b| fold_block(ctxt, a, b) ),
          fold_foreign_mod: |a,b| fold_foreign_mod(ctxt, a, b),
          fold_item_underscore: |a,b| fold_item_underscore(ctxt, a, b),
          .. *fold::default_ast_fold()};

    let fold = fold::make_fold(precursor);
    let res = @fold.fold_crate(*crate);
    return res;
}

fn filter_item(cx: ctxt, &&item: @ast::item) ->
   Option<@ast::item> {
    if item_in_cfg(cx, item) { option::Some(item) } else { option::None }
}

fn filter_view_item(cx: ctxt, &&view_item: @ast::view_item
                   )-> Option<@ast::view_item> {
    if view_item_in_cfg(cx, view_item) {
        option::Some(view_item)
    } else {
        option::None
    }
}

fn fold_mod(cx: ctxt, m: ast::_mod, fld: fold::ast_fold) ->
   ast::_mod {
    let filtered_items = vec::filter_map(m.items, |a| filter_item(cx, *a));
    let filtered_view_items = vec::filter_map(m.view_items,
                                              |a| filter_view_item(cx, *a));
    return {
        view_items: vec::map(filtered_view_items, |x| fld.fold_view_item(*x)),
        items: vec::filter_map(filtered_items, |x| fld.fold_item(*x))
    };
}

fn filter_foreign_item(cx: ctxt, &&item: @ast::foreign_item) ->
   Option<@ast::foreign_item> {
    if foreign_item_in_cfg(cx, item) {
        option::Some(item)
    } else { option::None }
}

fn fold_foreign_mod(cx: ctxt, nm: ast::foreign_mod,
                   fld: fold::ast_fold) -> ast::foreign_mod {
    let filtered_items = vec::filter_map(nm.items,
                                         |a| filter_foreign_item(cx, *a));
    let filtered_view_items = vec::filter_map(nm.view_items,
                                              |a| filter_view_item(cx, *a));
    return {
        sort: nm.sort,
        view_items: vec::map(filtered_view_items, |x| fld.fold_view_item(*x)),
        items: filtered_items
    };
}

fn fold_item_underscore(cx: ctxt, item: ast::item_,
                        fld: fold::ast_fold) -> ast::item_ {
    let item = match item {
        ast::item_impl(a, b, c, Some(methods)) => {
            let methods = methods.filter(|m| method_in_cfg(cx, *m) );
            ast::item_impl(a, b, c, Some(methods))
        }
        ast::item_trait(a, b, ref methods) => {
            let methods = methods.filter(|m| trait_method_in_cfg(cx, m) );
            ast::item_trait(a, b, methods)
        }
        _ => item
    };

    fold::noop_fold_item_underscore(item, fld)
}

fn filter_stmt(cx: ctxt, &&stmt: @ast::stmt) ->
   Option<@ast::stmt> {
    match stmt.node {
      ast::stmt_decl(decl, _) => {
        match decl.node {
          ast::decl_item(item) => {
            if item_in_cfg(cx, item) {
                option::Some(stmt)
            } else { option::None }
          }
          _ => option::Some(stmt)
        }
      }
      _ => option::Some(stmt)
    }
}

fn fold_block(cx: ctxt, b: ast::blk_, fld: fold::ast_fold) ->
   ast::blk_ {
    let filtered_stmts = vec::filter_map(b.stmts, |a| filter_stmt(cx, *a));
    return {view_items: b.view_items,
         stmts: vec::map(filtered_stmts, |x| fld.fold_stmt(*x)),
         expr: option::map(&b.expr, |x| fld.fold_expr(*x)),
         id: b.id,
         rules: b.rules};
}

fn item_in_cfg(cx: ctxt, item: @ast::item) -> bool {
    return cx.in_cfg(item.attrs);
}

fn foreign_item_in_cfg(cx: ctxt, item: @ast::foreign_item) -> bool {
    return cx.in_cfg(item.attrs);
}

fn view_item_in_cfg(cx: ctxt, item: @ast::view_item) -> bool {
    return cx.in_cfg(item.attrs);
}

fn method_in_cfg(cx: ctxt, meth: @ast::method) -> bool {
    return cx.in_cfg(meth.attrs);
}

fn trait_method_in_cfg(cx: ctxt, meth: &ast::trait_method) -> bool {
    match *meth {
        ast::required(ref meth) => cx.in_cfg(meth.attrs),
        ast::provided(@ref meth) => cx.in_cfg(meth.attrs)
    }
}

// Determine if an item should be translated in the current crate
// configuration based on the item's attributes
fn in_cfg(cfg: ast::crate_cfg, attrs: ~[ast::attribute]) -> bool {
    metas_in_cfg(cfg, attr::attr_metas(attrs))
}

fn metas_in_cfg(cfg: ast::crate_cfg, metas: ~[@ast::meta_item]) -> bool {

    // The "cfg" attributes on the item
    let cfg_metas = attr::find_meta_items_by_name(metas, ~"cfg");

    // Pull the inner meta_items from the #[cfg(meta_item, ...)]  attributes,
    // so we can match against them. This is the list of configurations for
    // which the item is valid
    let cfg_metas = vec::concat(vec::filter_map(cfg_metas,
        |i| attr::get_meta_item_list(*i)));

    let has_cfg_metas = vec::len(cfg_metas) > 0u;
    if !has_cfg_metas { return true; }

    for cfg_metas.each |cfg_mi| {
        if attr::contains(cfg, *cfg_mi) { return true; }
    }

    return false;
}

// Local Variables:
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
<|endoftext|>


<commit_before><commit_msg>Runtime::repository() should return HookResult<commit_after><|endoftext|>


<commit_before>use std::fmt;
use std::iter::FromIterator;
use std::result;
use std::str;
use std::str::FromStr;

use crate::headers_place::HeadersPlace;
use crate::req_resp::RequestOrResponse;

use crate::assert_types::*;

use bytes::Bytes;

use crate::solicit::header::method::{METHOD_GET, METHOD_POST};
use crate::solicit::header::name::HeaderName;
use crate::solicit::header::name::PseudoHeaderName;
use crate::solicit::header::name::PseudoHeaderNameSet;
use crate::HeaderValue;

pub(crate) mod method;
pub(crate) mod name;
pub(crate) mod value;

/// HTTP/2 header, regular or pseudo-header
#[derive(PartialEq, Eq, Hash, Clone)]
pub struct Header {
    name: HeaderName,
    pub value: HeaderValue,
}

impl fmt::Debug for Header {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Header")
            .field("name", &self.name.name())
            .field("value", &self.value)
            .finish()
    }
}

fn _assert_header_sync_send() {
    assert_sync::<Header>();
    assert_send::<Header>();
}

#[derive(Debug)]
pub enum HeaderError {
    UnknownPseudoHeader,
    EmptyName,
    EmptyValue(PseudoHeaderName),
    IncorrectCharInName,
    IncorrectCharInValue,
    HeaderNameNotAscii,
    HeaderValueNotAscii,
    UnexpectedPseudoHeader(PseudoHeaderName),
    PseudoHeadersInTrailers,
    PseudoHeadersAfterRegularHeaders,
    MoreThanOnePseudoHeader(PseudoHeaderName),
    MissingPseudoHeader(PseudoHeaderName),
    ConnectionSpecificHeader(&'static str),
    TeCanOnlyContainTrailer,
}

pub type HeaderResult<T> = result::Result<T, HeaderError>;

impl Header {
    /// Create a new `Header` object with exact values of `name` and `value`.
    ///
    /// This function performs header validation, in particular,
    /// header name must be lower case.
    pub fn new_validate(name: Bytes, value: Bytes) -> HeaderResult<Header> {
        let name = HeaderName::new_validate(name).map_err(|(e, _)| e)?;
        Ok(Header {
            name,
            value: HeaderValue::from(value),
        })
    }

    /// Creates a new `Header` with the given name and value.
    ///
    /// The name and value need to be convertible into a `HeaderPart`.
    ///
    /// Header name is converted to lower case.
    /// This function panics if header name is not valid.
    pub fn new<N: Into<HeaderName>, V: Into<HeaderValue>>(name: N, value: V) -> Header {
        Header {
            name: name.into(),
            value: value.into(),
        }
    }

    /// Construct a `:method` header
    fn method(value: impl Into<HeaderValue>) -> Header {
        Header::new(PseudoHeaderName::Method, value.into())
    }

    /// Construct a `:method` `GET` header
    fn method_get() -> Header {
        Header::method(METHOD_GET)
    }

    /// Construct a `:path` header
    fn path(path: impl Into<HeaderValue>) -> Header {
        Header::new(PseudoHeaderName::Path, path.into())
    }

    /// Construct a `:path` header
    fn status(path: impl Into<HeaderValue>) -> Header {
        Header::new(PseudoHeaderName::Status, path.into())
    }

    /// Return a borrowed representation of the `Header` name.
    pub fn name(&self) -> &str {
        self.name.name()
    }
    /// Return a borrowed representation of the `Header` value.
    pub fn value(&self) -> &[u8] {
        self.value.as_slice()
    }

    /// name: value
    pub fn format(&self) -> String {
        format!(
            "{}: {}",
            self.name(),
            String::from_utf8_lossy(self.value.as_slice())
        )
    }

    pub fn is_preudo_header(&self) -> bool {
        self.name.is_pseudo()
    }

    pub fn pseudo_header_name(&self) -> Option<PseudoHeaderName> {
        self.name.pseudo_header_name()
    }

    pub fn validate(&self, req_or_resp: RequestOrResponse) -> HeaderResult<()> {
        if let Some(h) = self.pseudo_header_name() {
            if h.req_or_resp() != req_or_resp {
                return Err(HeaderError::UnexpectedPseudoHeader(h));
            }
        }

        if req_or_resp == RequestOrResponse::Request && self.name() == "te" {
            // The only exception to this is the TE header field, which MAY be
            // present in an HTTP/2 request; when it is, it MUST NOT contain any
            // value other than "trailers".
            if self.value.as_slice() != b"trailers" {
                return Err(HeaderError::TeCanOnlyContainTrailer);
            }
        }

        Ok(())
    }
}

impl<N: Into<HeaderName>, V: Into<HeaderValue>> From<(N, V)> for Header {
    fn from(p: (N, V)) -> Header {
        Header::new(p.0, p.1)
    }
}

/// HTTP message headers (or trailers)
#[derive(Default, Debug, PartialEq, Eq, Clone)]
pub struct Headers {
    // Pseudo-headers stored before regular headers
    headers: Vec<Header>,
    pseudo_count: usize,
}

impl Headers {
    /// Construct empty headers
    pub fn new() -> Headers {
        Default::default()
    }

    /// Construct headers from a vec of individual headers
    pub fn from_vec(mut headers: Vec<Header>) -> Headers {
        headers.sort_by_key(|h| !h.is_preudo_header());
        let pseudo_count = headers.iter().take_while(|h| h.is_preudo_header()).count();
        Headers {
            headers,
            pseudo_count,
        }
    }

    pub(crate) fn from_vec_pseudo_first(headers: Vec<Header>) -> Result<Headers, HeaderError> {
        let mut saw_regular_header = false;
        let mut pseudo_count = 0;
        for header in &headers {
            if header.is_preudo_header() {
                if saw_regular_header {
                    return Err(HeaderError::PseudoHeadersAfterRegularHeaders);
                }
                pseudo_count += 1;
            } else {
                saw_regular_header = true;
            }
        }
        return Ok(Headers {
            headers,
            pseudo_count,
        });
    }

    /// Return an iterator over headers.
    ///
    /// Pseudo headers returned first.
    pub fn iter(&self) -> impl Iterator<Item = &Header> {
        self.headers.iter()
    }

    fn pseudo_headers(&self) -> &[Header] {
        &self.headers[..self.pseudo_count]
    }

    fn regular_headers(&self) -> &[Header] {
        &self.headers[self.pseudo_count..]
    }

    /// Dump all headers as multiline string.
    pub fn dump(&self) -> String {
        let mut r = String::new();
        for h in &self.headers {
            r.push_str(&h.format());
            r.push_str("\n");
        }
        r
    }

    /// Construct a `Headers` object with specified `:method` and `:path` headers
    pub fn new_get(path: impl Into<HeaderValue>) -> Headers {
        Headers::from_vec(vec![Header::method_get(), Header::path(path)])
    }

    /// Construct a `Headers` object with specified `:method` and `:path` headers
    pub fn new_post(path: impl Into<HeaderValue>) -> Headers {
        Headers::from_vec(vec![Header::method(METHOD_POST), Header::path(path)])
    }

    /// Construct a `Headers` object with single `:status` header
    pub fn new_status(code: u32) -> Headers {
        Headers::from_vec(vec![Header::status(format!("{}", code))])
    }

    /// Construct `:status 200` headers
    pub fn ok_200() -> Headers {
        Headers::new_status(200)
    }

    /// Construct `:status 404` headers
    pub fn not_found_404() -> Headers {
        Headers::new_status(404)
    }

    /// Construct `:status 500` headers
    pub fn internal_error_500() -> Headers {
        Headers::new_status(500)
    }

    /// Construct `:status 302; location: <location>` headers
    pub fn redirect_302(location: impl Into<HeaderValue>) -> Headers {
        let mut headers = Headers::new_status(302);
        headers.add("location", location);
        headers
    }

    pub(crate) fn validate(
        &self,
        req_or_resp: RequestOrResponse,
        headers_place: HeadersPlace,
    ) -> HeaderResult<()> {
        let mut pseudo_headers_met = PseudoHeaderNameSet::new();

        for header in self.pseudo_headers() {
            debug_assert!(header.is_preudo_header());

            header.validate(req_or_resp)?;

            let header_name = header.pseudo_header_name().unwrap();

            if headers_place == HeadersPlace::Trailing {
                return Err(HeaderError::PseudoHeadersInTrailers);
            }

            if !pseudo_headers_met.insert(header_name) {
                return Err(HeaderError::MoreThanOnePseudoHeader(header_name));
            }

            if header_name == PseudoHeaderName::Path {
                if header.value.as_slice().is_empty() {
                    return Err(HeaderError::EmptyValue(header_name));
                }
            }
        }

        for header in self.regular_headers() {
            header.validate(req_or_resp)?;
            debug_assert!(!header.is_preudo_header());
        }

        if headers_place == HeadersPlace::Initial {
            let required_headers = match req_or_resp {
                // All HTTP/2 requests MUST include exactly one valid value for the
                // ":method", ":scheme", and ":path" pseudo-header fields, unless it is
                // a CONNECT request (Section 8.3).  An HTTP request that omits
                // mandatory pseudo-header fields is malformed (Section 8.1.2.6).
                RequestOrResponse::Request => &[
                    PseudoHeaderName::Method,
                    PseudoHeaderName::Scheme,
                    PseudoHeaderName::Path,
                ][..],
                // For HTTP/2 responses, a single ":status" pseudo-header field is
                // defined that carries the HTTP status code field (see [RFC7231],
                // Section 6).  This pseudo-header field MUST be included in all
                // responses; otherwise, the response is malformed (Section 8.1.2.6).
                RequestOrResponse::Response => &[PseudoHeaderName::Status][..],
            };

            for &required in required_headers {
                if !pseudo_headers_met.contains(required) {
                    return Err(HeaderError::MissingPseudoHeader(required));
                }
            }
        }

        Ok(())
    }

    pub fn get_opt<'a>(&'a self, name: &str) -> Option<&'a str> {
        let headers = if name.starts_with(':') {
            self.pseudo_headers()
        } else {
            self.regular_headers()
        };
        headers
            .iter()
            .find(|h| h.name() == name)
            .and_then(|h| str::from_utf8(h.value()).ok())
    }

    pub fn get<'a>(&'a self, name: &str) -> &'a str {
        self.get_opt(name).unwrap()
    }

    pub fn get_opt_parse<I: FromStr>(&self, name: &str) -> Option<I> {
        self.get_opt(name).and_then(|h| h.parse().ok())
    }

    pub fn status(&self) -> u32 {
        self.get_opt_parse(":status").unwrap()
    }

    // TODO: return bytes, because headers it not require to be valid UTF-8
    pub fn path(&self) -> &str {
        self.get(":path")
    }

    pub fn method(&self) -> &str {
        self.get(":method")
    }

    pub fn content_length(&self) -> Option<u64> {
        match self.get_opt("content-length") {
            Some(v) => v.parse().ok(),
            None => None,
        }
    }

    /// Add a header
    pub fn add(&mut self, name: impl Into<HeaderName>, value: impl Into<HeaderValue>) {
        self.add_header(Header::new(name, value));
    }

    /// Add a header
    pub fn add_header(&mut self, header: Header) {
        if header.is_preudo_header() {
            let pseudo_count = self.pseudo_count;
            self.headers.insert(pseudo_count, header);
            self.pseudo_count += 1;
        } else {
            self.headers.push(header);
        }
    }

    /// Add all headers
    pub fn extend(&mut self, headers: Headers) {
        self.headers.reserve(headers.headers.len());
        for h in headers.headers {
            self.add_header(h);
        }
    }
}

impl FromIterator<Header> for Headers {
    fn from_iter<T: IntoIterator<Item = Header>>(iter: T) -> Headers {
        Headers::from_vec(iter.into_iter().collect())
    }
}

#[cfg(test)]
mod test {

    use crate::solicit::header::Header;

    #[test]
    fn test_partial_eq_of_headers() {
        let fully_static = Header::new(&b":method"[..], &b"GET"[..]);
        let static_name = Header::new(&b":method"[..], b"GET".to_vec());
        let other = Header::new(&b":path"[..], &b"/"[..]);

        assert_eq!(fully_static, static_name);
        assert_ne!(fully_static, other);
        assert_ne!(static_name, other);
    }

    #[test]
    fn test_debug() {
        assert_eq!(
            "Header { name: \":method\", value: \"GET\" }",
            format!("{:?}", Header::new(&b":method"[..], &b"GET"[..]))
        );
        assert_eq!(
            "Header { name: \":method\", value: \"\\t\" }",
            format!("{:?}", Header::new(&b":method"[..], &b"\t"[..]))
        );
    }
}
<commit_msg>Flatten imports<commit_after>use std::fmt;
use std::iter::FromIterator;
use std::result;
use std::str;
use std::str::FromStr;

use crate::headers_place::HeadersPlace;
use crate::req_resp::RequestOrResponse;

use crate::assert_types::*;

use bytes::Bytes;

use crate::solicit::header::method::METHOD_GET;
use crate::solicit::header::method::METHOD_POST;
use crate::solicit::header::name::HeaderName;
use crate::solicit::header::name::PseudoHeaderName;
use crate::solicit::header::name::PseudoHeaderNameSet;
use crate::HeaderValue;

pub(crate) mod method;
pub(crate) mod name;
pub(crate) mod value;

/// HTTP/2 header, regular or pseudo-header
#[derive(PartialEq, Eq, Hash, Clone)]
pub struct Header {
    name: HeaderName,
    pub value: HeaderValue,
}

impl fmt::Debug for Header {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Header")
            .field("name", &self.name.name())
            .field("value", &self.value)
            .finish()
    }
}

fn _assert_header_sync_send() {
    assert_sync::<Header>();
    assert_send::<Header>();
}

#[derive(Debug)]
pub enum HeaderError {
    UnknownPseudoHeader,
    EmptyName,
    EmptyValue(PseudoHeaderName),
    IncorrectCharInName,
    IncorrectCharInValue,
    HeaderNameNotAscii,
    HeaderValueNotAscii,
    UnexpectedPseudoHeader(PseudoHeaderName),
    PseudoHeadersInTrailers,
    PseudoHeadersAfterRegularHeaders,
    MoreThanOnePseudoHeader(PseudoHeaderName),
    MissingPseudoHeader(PseudoHeaderName),
    ConnectionSpecificHeader(&'static str),
    TeCanOnlyContainTrailer,
}

pub type HeaderResult<T> = result::Result<T, HeaderError>;

impl Header {
    /// Create a new `Header` object with exact values of `name` and `value`.
    ///
    /// This function performs header validation, in particular,
    /// header name must be lower case.
    pub fn new_validate(name: Bytes, value: Bytes) -> HeaderResult<Header> {
        let name = HeaderName::new_validate(name).map_err(|(e, _)| e)?;
        Ok(Header {
            name,
            value: HeaderValue::from(value),
        })
    }

    /// Creates a new `Header` with the given name and value.
    ///
    /// The name and value need to be convertible into a `HeaderPart`.
    ///
    /// Header name is converted to lower case.
    /// This function panics if header name is not valid.
    pub fn new<N: Into<HeaderName>, V: Into<HeaderValue>>(name: N, value: V) -> Header {
        Header {
            name: name.into(),
            value: value.into(),
        }
    }

    /// Construct a `:method` header
    fn method(value: impl Into<HeaderValue>) -> Header {
        Header::new(PseudoHeaderName::Method, value.into())
    }

    /// Construct a `:method` `GET` header
    fn method_get() -> Header {
        Header::method(METHOD_GET)
    }

    /// Construct a `:path` header
    fn path(path: impl Into<HeaderValue>) -> Header {
        Header::new(PseudoHeaderName::Path, path.into())
    }

    /// Construct a `:path` header
    fn status(path: impl Into<HeaderValue>) -> Header {
        Header::new(PseudoHeaderName::Status, path.into())
    }

    /// Return a borrowed representation of the `Header` name.
    pub fn name(&self) -> &str {
        self.name.name()
    }
    /// Return a borrowed representation of the `Header` value.
    pub fn value(&self) -> &[u8] {
        self.value.as_slice()
    }

    /// name: value
    pub fn format(&self) -> String {
        format!(
            "{}: {}",
            self.name(),
            String::from_utf8_lossy(self.value.as_slice())
        )
    }

    pub fn is_preudo_header(&self) -> bool {
        self.name.is_pseudo()
    }

    pub fn pseudo_header_name(&self) -> Option<PseudoHeaderName> {
        self.name.pseudo_header_name()
    }

    pub fn validate(&self, req_or_resp: RequestOrResponse) -> HeaderResult<()> {
        if let Some(h) = self.pseudo_header_name() {
            if h.req_or_resp() != req_or_resp {
                return Err(HeaderError::UnexpectedPseudoHeader(h));
            }
        }

        if req_or_resp == RequestOrResponse::Request && self.name() == "te" {
            // The only exception to this is the TE header field, which MAY be
            // present in an HTTP/2 request; when it is, it MUST NOT contain any
            // value other than "trailers".
            if self.value.as_slice() != b"trailers" {
                return Err(HeaderError::TeCanOnlyContainTrailer);
            }
        }

        Ok(())
    }
}

impl<N: Into<HeaderName>, V: Into<HeaderValue>> From<(N, V)> for Header {
    fn from(p: (N, V)) -> Header {
        Header::new(p.0, p.1)
    }
}

/// HTTP message headers (or trailers)
#[derive(Default, Debug, PartialEq, Eq, Clone)]
pub struct Headers {
    // Pseudo-headers stored before regular headers
    headers: Vec<Header>,
    pseudo_count: usize,
}

impl Headers {
    /// Construct empty headers
    pub fn new() -> Headers {
        Default::default()
    }

    /// Construct headers from a vec of individual headers
    pub fn from_vec(mut headers: Vec<Header>) -> Headers {
        headers.sort_by_key(|h| !h.is_preudo_header());
        let pseudo_count = headers.iter().take_while(|h| h.is_preudo_header()).count();
        Headers {
            headers,
            pseudo_count,
        }
    }

    pub(crate) fn from_vec_pseudo_first(headers: Vec<Header>) -> Result<Headers, HeaderError> {
        let mut saw_regular_header = false;
        let mut pseudo_count = 0;
        for header in &headers {
            if header.is_preudo_header() {
                if saw_regular_header {
                    return Err(HeaderError::PseudoHeadersAfterRegularHeaders);
                }
                pseudo_count += 1;
            } else {
                saw_regular_header = true;
            }
        }
        return Ok(Headers {
            headers,
            pseudo_count,
        });
    }

    /// Return an iterator over headers.
    ///
    /// Pseudo headers returned first.
    pub fn iter(&self) -> impl Iterator<Item = &Header> {
        self.headers.iter()
    }

    fn pseudo_headers(&self) -> &[Header] {
        &self.headers[..self.pseudo_count]
    }

    fn regular_headers(&self) -> &[Header] {
        &self.headers[self.pseudo_count..]
    }

    /// Dump all headers as multiline string.
    pub fn dump(&self) -> String {
        let mut r = String::new();
        for h in &self.headers {
            r.push_str(&h.format());
            r.push_str("\n");
        }
        r
    }

    /// Construct a `Headers` object with specified `:method` and `:path` headers
    pub fn new_get(path: impl Into<HeaderValue>) -> Headers {
        Headers::from_vec(vec![Header::method_get(), Header::path(path)])
    }

    /// Construct a `Headers` object with specified `:method` and `:path` headers
    pub fn new_post(path: impl Into<HeaderValue>) -> Headers {
        Headers::from_vec(vec![Header::method(METHOD_POST), Header::path(path)])
    }

    /// Construct a `Headers` object with single `:status` header
    pub fn new_status(code: u32) -> Headers {
        Headers::from_vec(vec![Header::status(format!("{}", code))])
    }

    /// Construct `:status 200` headers
    pub fn ok_200() -> Headers {
        Headers::new_status(200)
    }

    /// Construct `:status 404` headers
    pub fn not_found_404() -> Headers {
        Headers::new_status(404)
    }

    /// Construct `:status 500` headers
    pub fn internal_error_500() -> Headers {
        Headers::new_status(500)
    }

    /// Construct `:status 302; location: <location>` headers
    pub fn redirect_302(location: impl Into<HeaderValue>) -> Headers {
        let mut headers = Headers::new_status(302);
        headers.add("location", location);
        headers
    }

    pub(crate) fn validate(
        &self,
        req_or_resp: RequestOrResponse,
        headers_place: HeadersPlace,
    ) -> HeaderResult<()> {
        let mut pseudo_headers_met = PseudoHeaderNameSet::new();

        for header in self.pseudo_headers() {
            debug_assert!(header.is_preudo_header());

            header.validate(req_or_resp)?;

            let header_name = header.pseudo_header_name().unwrap();

            if headers_place == HeadersPlace::Trailing {
                return Err(HeaderError::PseudoHeadersInTrailers);
            }

            if !pseudo_headers_met.insert(header_name) {
                return Err(HeaderError::MoreThanOnePseudoHeader(header_name));
            }

            if header_name == PseudoHeaderName::Path {
                if header.value.as_slice().is_empty() {
                    return Err(HeaderError::EmptyValue(header_name));
                }
            }
        }

        for header in self.regular_headers() {
            header.validate(req_or_resp)?;
            debug_assert!(!header.is_preudo_header());
        }

        if headers_place == HeadersPlace::Initial {
            let required_headers = match req_or_resp {
                // All HTTP/2 requests MUST include exactly one valid value for the
                // ":method", ":scheme", and ":path" pseudo-header fields, unless it is
                // a CONNECT request (Section 8.3).  An HTTP request that omits
                // mandatory pseudo-header fields is malformed (Section 8.1.2.6).
                RequestOrResponse::Request => &[
                    PseudoHeaderName::Method,
                    PseudoHeaderName::Scheme,
                    PseudoHeaderName::Path,
                ][..],
                // For HTTP/2 responses, a single ":status" pseudo-header field is
                // defined that carries the HTTP status code field (see [RFC7231],
                // Section 6).  This pseudo-header field MUST be included in all
                // responses; otherwise, the response is malformed (Section 8.1.2.6).
                RequestOrResponse::Response => &[PseudoHeaderName::Status][..],
            };

            for &required in required_headers {
                if !pseudo_headers_met.contains(required) {
                    return Err(HeaderError::MissingPseudoHeader(required));
                }
            }
        }

        Ok(())
    }

    pub fn get_opt<'a>(&'a self, name: &str) -> Option<&'a str> {
        let headers = if name.starts_with(':') {
            self.pseudo_headers()
        } else {
            self.regular_headers()
        };
        headers
            .iter()
            .find(|h| h.name() == name)
            .and_then(|h| str::from_utf8(h.value()).ok())
    }

    pub fn get<'a>(&'a self, name: &str) -> &'a str {
        self.get_opt(name).unwrap()
    }

    pub fn get_opt_parse<I: FromStr>(&self, name: &str) -> Option<I> {
        self.get_opt(name).and_then(|h| h.parse().ok())
    }

    pub fn status(&self) -> u32 {
        self.get_opt_parse(":status").unwrap()
    }

    // TODO: return bytes, because headers it not require to be valid UTF-8
    pub fn path(&self) -> &str {
        self.get(":path")
    }

    pub fn method(&self) -> &str {
        self.get(":method")
    }

    pub fn content_length(&self) -> Option<u64> {
        match self.get_opt("content-length") {
            Some(v) => v.parse().ok(),
            None => None,
        }
    }

    /// Add a header
    pub fn add(&mut self, name: impl Into<HeaderName>, value: impl Into<HeaderValue>) {
        self.add_header(Header::new(name, value));
    }

    /// Add a header
    pub fn add_header(&mut self, header: Header) {
        if header.is_preudo_header() {
            let pseudo_count = self.pseudo_count;
            self.headers.insert(pseudo_count, header);
            self.pseudo_count += 1;
        } else {
            self.headers.push(header);
        }
    }

    /// Add all headers
    pub fn extend(&mut self, headers: Headers) {
        self.headers.reserve(headers.headers.len());
        for h in headers.headers {
            self.add_header(h);
        }
    }
}

impl FromIterator<Header> for Headers {
    fn from_iter<T: IntoIterator<Item = Header>>(iter: T) -> Headers {
        Headers::from_vec(iter.into_iter().collect())
    }
}

#[cfg(test)]
mod test {

    use crate::solicit::header::Header;

    #[test]
    fn test_partial_eq_of_headers() {
        let fully_static = Header::new(&b":method"[..], &b"GET"[..]);
        let static_name = Header::new(&b":method"[..], b"GET".to_vec());
        let other = Header::new(&b":path"[..], &b"/"[..]);

        assert_eq!(fully_static, static_name);
        assert_ne!(fully_static, other);
        assert_ne!(static_name, other);
    }

    #[test]
    fn test_debug() {
        assert_eq!(
            "Header { name: \":method\", value: \"GET\" }",
            format!("{:?}", Header::new(&b":method"[..], &b"GET"[..]))
        );
        assert_eq!(
            "Header { name: \":method\", value: \"\\t\" }",
            format!("{:?}", Header::new(&b":method"[..], &b"\t"[..]))
        );
    }
}
<|endoftext|>


<commit_before>use std::mem;
use std::ops;
use std::ptr;
use std::slice;

use nodrop::NoDrop;

use num_traits::{Unsigned, Zero, One};

pub struct OverflowError<T1, T2>(pub T1, pub T2);

pub enum InsertError<T1, T2> {
    Overflow(T1, T2),
    OutOfBounds(T1, T2)
}

pub unsafe trait Array {
    type Item;
    type Index: Index;
    fn as_ptr(&self) -> *const Self::Item;
    fn as_mut_ptr(&mut self) -> *mut Self::Item;
    fn capacity(&self) -> usize;
}

pub trait Index : Unsigned + ops::AddAssign + ops::SubAssign + Copy {
    fn as_usize(self) -> usize;
    fn from_usize(value: usize) -> Self;
}

macro_rules! impl_index {
    ($index_type:ty) => (
        impl Index for $index_type {
            fn as_usize(self) -> usize {
                self as usize
            }

            fn from_usize(value: usize) -> Self {
                value as $index_type
            }
        }
    )
}

impl_index!(u8);
impl_index!(u16);
impl_index!(u32);
impl_index!(usize);

macro_rules! impl_array {
    ($index_type:ty, $len:expr ) => (
        unsafe impl<T> Array for [T; $len] {
            type Item = T;
            type Index = $index_type;
            #[inline(always)]
            fn as_ptr(&self) -> *const T {
                self as *const _ as *const _
            }

            #[inline(always)]
            fn as_mut_ptr(&mut self) -> *mut T {
                self as *mut _ as *mut _
            }

            #[inline(always)]
            fn capacity(&self) -> usize {
                $len
            }
        }
    )
}

macro_rules! impl_array_many {
    ($index_type:ty, $($len:expr),+) => (
        $(
            impl_array!($index_type, $len);
        )*
    );
}

impl_array_many!(u8, 1, 2, 7, 31, 256);

pub struct LockstepArray<A1: Array, A2: Array> {
    len: A1::Index,
    array1: NoDrop<A1>,
    array2: NoDrop<A2>
}

impl<A1, A2> LockstepArray<A1, A2> where A1: Array, A2: Array {
    pub fn new() -> LockstepArray<A1, A2> {
        LockstepArray {
            len: Zero::zero(),
            array1: NoDrop::new(unsafe {mem::uninitialized()}),
            array2: NoDrop::new(unsafe {mem::uninitialized()})
        }
    }

    pub fn from_arrays<B1, B2>(array1: B1, array2: B2) -> LockstepArray<A1, A2>
        where B1: Array<Item=A1::Item>, B2: Array<Item=A2::Item> {
        let mut lockstep = LockstepArray::<A1, A2>::new();
        // Eventually these will be encoded into the type system
        assert!(array1.capacity() <= lockstep.array1.capacity());
        assert!(array2.capacity() <= lockstep.array2.capacity());
        assert_eq!(array1.capacity(), array2.capacity());
        let len = A1::Index::from_usize(array1.capacity());
        unsafe {
            ptr::copy_nonoverlapping(array1.as_ptr(),
                                     lockstep.array1.as_mut_ptr(),
                                     array1.capacity());
            mem::forget(array1);
            ptr::copy_nonoverlapping(array2.as_ptr(),
                                     lockstep.array2.as_mut_ptr(),
                                     array2.capacity());
            mem::forget(array2);
        }
        lockstep.len = len;
        lockstep
    }

    pub fn push(&mut self, item1: A1::Item, item2: A2::Item)
                -> Result<(), OverflowError<A1::Item, A2::Item>> {
        if self.len.as_usize() == self.array1.capacity() {
            return Err(OverflowError(item1, item2));
        }
        unsafe {
            let p1 = self.array1.as_mut_ptr();
            ptr::write(p1.offset(self.len.as_usize() as isize), item1);
            let p2 = self.array2.as_mut_ptr();
            ptr::write(p2.offset(self.len.as_usize() as isize), item2);
        }
        self.len += One::one();
        Ok(())
    }

    pub fn pop(&mut self) -> Option<(A1::Item, A2::Item)> {
        if self.len == Zero::zero() {
            None
        } else {
            unsafe {
                // Calculate item 1's pointer
                let src1 = self.array1.as_mut_ptr()
                    .offset(self.len.as_usize() as isize);
                // Move item 1 out of the pointer
                let item1 = ptr::read(src1);
                // Calculate item 1's pointer
                let src2 = self.array2.as_mut_ptr()
                    .offset(self.len.as_usize() as isize);
                // Move item 1 out of the pointer
                let item2 = ptr::read(src2);
                // Update length
                self.len -= One::one();
                Some((item1, item2))
            }
        }
    }

    pub fn insert(&mut self, index: usize, item1: A1::Item, item2: A2::Item)
                  -> Result<(), InsertError<A1::Item, A2::Item>> {
        if index > self.len.as_usize() {
            Err(InsertError::OutOfBounds(item1, item2))
        } else if self.len.as_usize() == self.array1.capacity() {
            Err(InsertError::Overflow(item1, item2))
        } else {
            unsafe {
                // TODO: Tricky math, check indices and such
                let dest1 = self.array1.as_mut_ptr().offset(index as isize);
                ptr::copy(dest1, dest1.offset(1), self.len.as_usize() - index);
                ptr::write(dest1, item1);
                let dest2 = self.array2.as_mut_ptr().offset(index as isize);
                ptr::copy(dest2, dest2.offset(1), self.len.as_usize() - index);
                ptr::write(dest2, item2);
            }
            self.len += One::one();
            Ok(())
        }
    }

    pub fn remove(&mut self, index: usize) -> Option<(A1::Item, A2::Item)> {
        if index >= self.len.as_usize() {
            None
        } else {
            let item1;
            let item2;
            unsafe {
                // Calculate item 1's pointer
                let src1 = self.array1.as_mut_ptr().offset(index as isize);
                // Move item 1 out of the pointer
                item1 = ptr::read(src1);
                // Shift down array 1 down by 1
                // Is self.len - index correct?
                ptr::copy(src1.offset(1), src1, self.len.as_usize() - index);
                // Calculate item 1's pointer
                let src2 = self.array2.as_mut_ptr().offset(index as isize);
                // Move item 1 out of the pointer
                item2 = ptr::read(src2);
                // Shift down array 1 down by 1
                // Is self.len - index correct?
                ptr::copy(src2, src2.offset(1), self.len.as_usize() - index);
                // Update length
                self.len -= One::one();
            }
            Some((item1, item2))
        }
    }

    pub fn is_empty(&self) -> bool {
        return self.len != Zero::zero()
    }

    pub fn len(&self) -> usize {
        return self.len.as_usize()
    }

    pub fn array1(&self) -> &[A1::Item] {
        unsafe {
            let ptr = self.array1.as_ptr();
            let len = self.len.as_usize();
            slice::from_raw_parts(ptr, len)
        }
    }

    pub fn array2(&self) -> &[A2::Item] {
        unsafe {
            let ptr = self.array2.as_ptr();
            let len = self.len.as_usize();
            slice::from_raw_parts(ptr, len)
        }
    }

    pub fn array1_mut(&mut self) -> &mut [A1::Item] {
        unsafe {
            let ptr = self.array1.as_mut_ptr();
            let len = self.len.as_usize();
            slice::from_raw_parts_mut(ptr, len)
        }
    }

    pub fn array2_mut(&mut self) -> &mut [A2::Item] {
        unsafe {
            let ptr = self.array2.as_mut_ptr();
            let len = self.len.as_usize();
            slice::from_raw_parts_mut(ptr, len)
        }
    }

    pub fn capacity(&self) -> usize {
        self.array1.capacity()
    }
}

impl<A1, A2> Default for LockstepArray<A1, A2> where A1: Array, A2: Array {
    fn default() -> Self {
        Self::new()
    }
}

impl<A1, A2> IntoIterator for LockstepArray<A1, A2> where A1: Array, A2: Array {
    type Item = (A1::Item, A2::Item);
    type IntoIter = IntoIter<A1, A2>;
    fn into_iter(self) -> Self::IntoIter {
        let LockstepArray { len, array1, array2 } = self;
        IntoIter {
            len: len.as_usize(),
            array1: array1,
            array2: array2,
            pos: 0
        }
    }
}

pub struct IntoIter<A1: Array, A2: Array> {
    len: usize,
    array1: NoDrop<A1>,
    array2: NoDrop<A2>,
    pos: usize
}

impl<A1: Array, A2: Array> Iterator for IntoIter<A1, A2> {
    type Item = (A1::Item, A2::Item);
    fn next(&mut self) -> Option<Self::Item> {
        if self.pos == self.len {
            None
        } else {
            unsafe {
                let ptr1 = self.array1.as_mut_ptr().offset(self.pos as isize);
                let item1 = ptr::read(ptr1);
                let ptr2 = self.array2.as_mut_ptr().offset(self.pos as isize);
                let item2 = ptr::read(ptr2);
                self.pos += 1;
                Some((item1, item2))
            }
        }
    }
}
<commit_msg>Unit tests for locksteparray::IntoIter<commit_after>use std::mem;
use std::ops;
use std::ptr;
use std::slice;
use std::iter;

use nodrop::NoDrop;

use num_traits::{Unsigned, Zero, One};

pub struct OverflowError<T1, T2>(pub T1, pub T2);

#[derive(Debug)]
pub enum InsertError<T1, T2> {
    Overflow(T1, T2),
    OutOfBounds(T1, T2)
}

pub unsafe trait Array {
    type Item;
    type Index: Index;
    fn as_ptr(&self) -> *const Self::Item;
    fn as_mut_ptr(&mut self) -> *mut Self::Item;
    fn capacity(&self) -> usize;
}

pub trait Index : Unsigned + ops::AddAssign + ops::SubAssign + Copy {
    fn as_usize(self) -> usize;
    fn from_usize(value: usize) -> Self;
}

macro_rules! impl_index {
    ($index_type:ty) => (
        impl Index for $index_type {
            fn as_usize(self) -> usize {
                self as usize
            }

            fn from_usize(value: usize) -> Self {
                value as $index_type
            }
        }
    )
}

impl_index!(u8);
impl_index!(u16);
impl_index!(u32);
impl_index!(usize);

macro_rules! impl_array {
    ($index_type:ty, $len:expr ) => (
        unsafe impl<T> Array for [T; $len] {
            type Item = T;
            type Index = $index_type;
            #[inline(always)]
            fn as_ptr(&self) -> *const T {
                self as *const _ as *const _
            }

            #[inline(always)]
            fn as_mut_ptr(&mut self) -> *mut T {
                self as *mut _ as *mut _
            }

            #[inline(always)]
            fn capacity(&self) -> usize {
                $len
            }
        }
    )
}

macro_rules! impl_array_many {
    ($index_type:ty, $($len:expr),+) => (
        $(
            impl_array!($index_type, $len);
        )*
    );
}

impl_array_many!(u8, 1, 2, 7, 31, 256);

pub struct LockstepArray<A1: Array, A2: Array> {
    len: A1::Index,
    array1: NoDrop<A1>,
    array2: NoDrop<A2>
}

impl<A1, A2> LockstepArray<A1, A2> where A1: Array, A2: Array {
    pub fn new() -> LockstepArray<A1, A2> {
        LockstepArray {
            len: Zero::zero(),
            array1: NoDrop::new(unsafe {mem::uninitialized()}),
            array2: NoDrop::new(unsafe {mem::uninitialized()})
        }
    }

    pub fn from_arrays<B1, B2>(array1: B1, array2: B2) -> LockstepArray<A1, A2>
        where B1: Array<Item=A1::Item>, B2: Array<Item=A2::Item> {
        let mut lockstep = LockstepArray::<A1, A2>::new();
        // Eventually these will be encoded into the type system
        assert!(array1.capacity() <= lockstep.array1.capacity());
        assert!(array2.capacity() <= lockstep.array2.capacity());
        assert_eq!(array1.capacity(), array2.capacity());
        let len = A1::Index::from_usize(array1.capacity());
        unsafe {
            ptr::copy_nonoverlapping(array1.as_ptr(),
                                     lockstep.array1.as_mut_ptr(),
                                     array1.capacity());
            mem::forget(array1);
            ptr::copy_nonoverlapping(array2.as_ptr(),
                                     lockstep.array2.as_mut_ptr(),
                                     array2.capacity());
            mem::forget(array2);
        }
        lockstep.len = len;
        lockstep
    }

    pub fn push(&mut self, item1: A1::Item, item2: A2::Item)
                -> Result<(), OverflowError<A1::Item, A2::Item>> {
        if self.len.as_usize() == self.array1.capacity() {
            return Err(OverflowError(item1, item2));
        }
        unsafe {
            let p1 = self.array1.as_mut_ptr();
            ptr::write(p1.offset(self.len.as_usize() as isize), item1);
            let p2 = self.array2.as_mut_ptr();
            ptr::write(p2.offset(self.len.as_usize() as isize), item2);
        }
        self.len += One::one();
        Ok(())
    }

    pub fn pop(&mut self) -> Option<(A1::Item, A2::Item)> {
        if self.len == Zero::zero() {
            None
        } else {
            unsafe {
                // Calculate item 1's pointer
                let src1 = self.array1.as_mut_ptr()
                    .offset(self.len.as_usize() as isize);
                // Move item 1 out of the pointer
                let item1 = ptr::read(src1);
                // Calculate item 1's pointer
                let src2 = self.array2.as_mut_ptr()
                    .offset(self.len.as_usize() as isize);
                // Move item 1 out of the pointer
                let item2 = ptr::read(src2);
                // Update length
                self.len -= One::one();
                Some((item1, item2))
            }
        }
    }

    pub fn insert(&mut self, index: usize, item1: A1::Item, item2: A2::Item)
                  -> Result<(), InsertError<A1::Item, A2::Item>> {
        if index > self.len.as_usize() {
            Err(InsertError::OutOfBounds(item1, item2))
        } else if self.len.as_usize() == self.array1.capacity() {
            Err(InsertError::Overflow(item1, item2))
        } else {
            unsafe {
                // TODO: Tricky math, check indices and such
                let dest1 = self.array1.as_mut_ptr().offset(index as isize);
                ptr::copy(dest1, dest1.offset(1), self.len.as_usize() - index);
                ptr::write(dest1, item1);
                let dest2 = self.array2.as_mut_ptr().offset(index as isize);
                ptr::copy(dest2, dest2.offset(1), self.len.as_usize() - index);
                ptr::write(dest2, item2);
            }
            self.len += One::one();
            Ok(())
        }
    }

    pub fn remove(&mut self, index: usize) -> Option<(A1::Item, A2::Item)> {
        if index >= self.len.as_usize() {
            None
        } else {
            let item1;
            let item2;
            unsafe {
                // Calculate item 1's pointer
                let src1 = self.array1.as_mut_ptr().offset(index as isize);
                // Move item 1 out of the pointer
                item1 = ptr::read(src1);
                // Shift down array 1 down by 1
                // Is self.len - index correct?
                ptr::copy(src1.offset(1), src1, self.len.as_usize() - index);
                // Calculate item 1's pointer
                let src2 = self.array2.as_mut_ptr().offset(index as isize);
                // Move item 1 out of the pointer
                item2 = ptr::read(src2);
                // Shift down array 1 down by 1
                // Is self.len - index correct?
                ptr::copy(src2, src2.offset(1), self.len.as_usize() - index);
                // Update length
                self.len -= One::one();
            }
            Some((item1, item2))
        }
    }

    pub fn is_empty(&self) -> bool {
        return self.len != Zero::zero()
    }

    pub fn len(&self) -> usize {
        return self.len.as_usize()
    }

    pub fn array1(&self) -> &[A1::Item] {
        unsafe {
            let ptr = self.array1.as_ptr();
            let len = self.len.as_usize();
            slice::from_raw_parts(ptr, len)
        }
    }

    pub fn array2(&self) -> &[A2::Item] {
        unsafe {
            let ptr = self.array2.as_ptr();
            let len = self.len.as_usize();
            slice::from_raw_parts(ptr, len)
        }
    }

    pub fn array1_mut(&mut self) -> &mut [A1::Item] {
        unsafe {
            let ptr = self.array1.as_mut_ptr();
            let len = self.len.as_usize();
            slice::from_raw_parts_mut(ptr, len)
        }
    }

    pub fn array2_mut(&mut self) -> &mut [A2::Item] {
        unsafe {
            let ptr = self.array2.as_mut_ptr();
            let len = self.len.as_usize();
            slice::from_raw_parts_mut(ptr, len)
        }
    }

    pub fn capacity(&self) -> usize {
        self.array1.capacity()
    }
}

impl<A1, A2> Default for LockstepArray<A1, A2> where A1: Array, A2: Array {
    fn default() -> Self {
        Self::new()
    }
}

impl<A1, A2> IntoIterator for LockstepArray<A1, A2> where A1: Array, A2: Array {
    type Item = (A1::Item, A2::Item);
    type IntoIter = IntoIter<A1, A2>;
    fn into_iter(self) -> Self::IntoIter {
        let LockstepArray { len, array1, array2 } = self;
        IntoIter {
            len: len.as_usize(),
            array1: array1,
            array2: array2,
            pos: 0
        }
    }
}

pub struct IntoIter<A1: Array, A2: Array> {
    len: usize,
    array1: NoDrop<A1>,
    array2: NoDrop<A2>,
    pos: usize
}

impl<A1: Array, A2: Array> Iterator for IntoIter<A1, A2> {
    type Item = (A1::Item, A2::Item);
    fn next(&mut self) -> Option<Self::Item> {
        if self.pos == self.len {
            None
        } else {
            unsafe {
                let ptr1 = self.array1.as_mut_ptr().offset(self.pos as isize);
                let item1 = ptr::read(ptr1);
                let ptr2 = self.array2.as_mut_ptr().offset(self.pos as isize);
                let item2 = ptr::read(ptr2);
                self.pos += 1;
                Some((item1, item2))
            }
        }
    }
}

#[test]
fn test_into_iter() {
    let mut locksteparray = LockstepArray::<[u8; 7], [u8; 7]>::new();
    locksteparray.insert(0, 4, 5).unwrap();
    locksteparray.insert(1, 3, 4).unwrap();
    let mut iter = locksteparray.into_iter();
    assert_eq!(iter.next(), Some((4, 5)));
    assert_eq!(iter.next(), Some((3, 4)));
    assert_eq!(iter.next(), None);
}
<|endoftext|>