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

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

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

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

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

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

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

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

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

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

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

pub type GetPackageTrackingDetailsResponse = PackageTrackingDetails;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

pub type GetPackageTrackingDetailsResponse = PackageTrackingDetails;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


<commit_before>use orbtk::{
    prelude::*,
    // only instantiate the `default` theme
    widgets::themes::theme_orbtk::{
        register_default_fonts, THEME_DEFAULT, THEME_DEFAULT_COLORS_DARK, THEME_DEFAULT_FONTS,
    },
};

// German localization file.
static POPUP_DE_DE: &str = include_str!("assets/popup/popup_de_DE.ron");

static DARK_EXT: &str = include_str!("assets/popup/default_dark.ron");

static ID_BUTTON: &str = "Toggle Popup";
static ID_GRID: &str = "PopupGrid";
static ID_GRID_PLACEMENT: &str = "Selection GRID";
static ID_MAIN_VIEW: &str = "MainView";
static ID_POPUP: &str = "Popup";
static ID_POPUP_STACK: &str = "PopupStack";
static ID_PLACEMENT_COMBO_BOX: &str = "Placement Selecton Box";
static ID_PLACEMENT_OFFSET: &str = "Placement Offset";
static ID_PLACEMENT_OFFSET_LABEL: &str = "Offset";
static ID_PLACEMENT_COMBO_BOX_LABEL: &str = "Placement Label";
static ID_TARGET: &str = "Target";

/// Valid `actions` that are handled as state changes in the `MainView` widget.
#[derive(Copy, Clone)]
enum MainViewAction {
    /// Toggle the visibility of the popup
    TogglePopup,

    /// Update the offset of the popup relative to the target
    UpdateOffset,

    /// Update the placement of the popup relative to the target
    UpdatePlacement,
}

/// Valid `structures` that are handled inside the state of the `MainView` widget.
#[derive(AsAny, Default)]
struct MainViewState {
    _action: Option<MainViewAction>,
}

/// Method definitions, that react on any given state change inside the `MainView` widget.
impl MainViewState {
    fn _action(&mut self, action: impl Into<Option<MainViewAction>>) {
        self._action = action.into();
    }

    fn toggle_popup(&mut self, ctx: &mut Context<'_>) {
        let open = ctx.child(ID_POPUP).clone::<bool>("open");

        if open {
            Popup::open_set(&mut ctx.child(ID_POPUP), false);
            Popup::visibility_set(&mut ctx.child(ID_POPUP), Visibility::Collapsed);
            Button::text_set(&mut ctx.child(ID_BUTTON), "Click me to show the popup");
        } else {
            Popup::visibility_set(&mut ctx.child(ID_POPUP), Visibility::Visible);
            Popup::open_set(&mut ctx.child(ID_POPUP), true);
            Button::text_set(&mut ctx.child(ID_BUTTON), "Click me to hide the popup");
        }
    }

    fn update_placement(&mut self, ctx: &mut Context<'_>) {
        let selected_index: i32 = ctx.child(ID_PLACEMENT_COMBO_BOX).clone("selected_index");
        let placement: Placement = ctx.child(ID_POPUP).clone_or_default("placement");
        match selected_index {
            0 => Popup::placement_set(&mut ctx.child(ID_POPUP), placement.bottom()),
            1 => Popup::placement_set(&mut ctx.child(ID_POPUP), placement.left()),
            2 => Popup::placement_set(&mut ctx.child(ID_POPUP), placement.right()),
            3 => Popup::placement_set(&mut ctx.child(ID_POPUP), placement.top()),
            _ => panic!(),
        }
        if cfg!(debug) {
            println!(
                "popup_v2: Placement {:?} (index: {:?}).",
                ctx.child(ID_POPUP).get::<Placement>("placement"),
                selected_index
            );
        }
    }

    fn update_offset(&mut self, ctx: &mut Context<'_>) {
        let offset: f64 = ctx.child(ID_PLACEMENT_OFFSET).clone("val");
        Popup::offset_set(&mut ctx.child(ID_POPUP), offset);

        if cfg!(debug) {
            println!(
                "popup_v2: Offset {:?}.",
                ctx.child(ID_POPUP).get::<f64>("offset")
            );
        }
    }
}

/// Associated methods handled inside the `MainViewState`
impl State for MainViewState {
    fn init(&mut self, _registry: &mut Registry, ctx: &mut Context) {
        // initialize the popup properties
        let offset: f64 = ctx.child(ID_PLACEMENT_OFFSET).clone("val");
        Popup::offset_set(&mut ctx.child(ID_POPUP), offset);
        Popup::open_set(&mut ctx.child(ID_POPUP), false);
        Popup::visibility_set(&mut ctx.child(ID_POPUP), Visibility::Collapsed);
    }

    fn messages(
        &mut self,
        mut messages: MessageReader,
        _registry: &mut Registry,
        ctx: &mut Context,
    ) {
        for message in messages.read::<MainViewAction>() {
            match message {
                MainViewAction::TogglePopup => {
                    MainViewState::toggle_popup(self, ctx);
                }
                MainViewAction::UpdatePlacement => {
                    MainViewState::update_placement(self, ctx);
                }
                MainViewAction::UpdateOffset => {
                    MainViewState::update_offset(self, ctx);
                }
            }
        }
    }
}

fn theme() -> Theme {
    register_default_fonts(Theme::from_config(
        ThemeConfig::from(DARK_EXT)
            .extend(ThemeConfig::from(THEME_DEFAULT))
            .extend(ThemeConfig::from(THEME_DEFAULT_COLORS_DARK))
            .extend(ThemeConfig::from(THEME_DEFAULT_FONTS)),
    ))
}

// Used to define ComboBox list members
type List = Vec<String>;

// constructs the MainView
widget!(
    MainView<MainViewState> {
    /// Active seleced index of combo box.
    selected_index: i32,

    /// Acitve selected position of popup.
    placements: List
    }
);

/// The template implementation of the main view
/// GUI elements are styled using the "style" attribute referencing to a ron based css
impl Template for MainView {
    fn template(self, id: Entity, ctx: &mut BuildContext) -> Self {
        let placements = vec![
            // TODO: make it sort independent
            "Bottom".to_string(),
            "Left".to_string(),
            "Right".to_string(),
            "Top".to_string(),
        ];
        let count_placements = placements.len();

        let target_container = Container::new()
            .id(ID_TARGET)
            .attach(Grid::row(2))
            .attach(Grid::column(1))
            .style("container_form")
            .child(
                Stack::new()
                    .orientation("vertical")
                    .spacing(16.0)
                    .child(
                        TextBlock::new()
                            .style("target_text_block")
                            .text("Target")
                            .build(ctx),
                    )
                    .child(
                        TextBlock::new()
                            .style("target_text_block")
                            .text("Target content")
                            .build(ctx),
                    )
                    .build(ctx),
            )
            .build(ctx);

        let popup = Popup::new()
            .id(ID_POPUP)
            .name(ID_POPUP)
            .style("popup_form")
            // alternative: popup is target of given coordinates (Point)
            //.target(Point::new(0.0, 0.0))
            .height(180.0)
            .open(false)
            .width(180.0)
            .target(target_container)
            .child(
                Container::new()
                    .child(
                        Stack::new()
                            .id(ID_POPUP_STACK)
                            .orientation("vertical")
                            .spacing(16.0)
                            .child(
                                TextBlock::new()
                                    .style("popup_text_block")
                                    .h_align("center")
                                    .v_align("center")
                                    .text("Popup Header")
                                    .build(ctx),
                            )
                            .child(
                                TextBlock::new()
                                    .style("popup_text_block")
                                    .h_align("center")
                                    .v_align("center")
                                    .text("Popup content")
                                    .build(ctx),
                            )
                            .build(ctx),
                    )
                    .build(ctx),
            )
            .build(ctx);

        let _ = ctx.append_child_to_overlay(popup);

        // Widget:  MainView
        self.id(ID_MAIN_VIEW)
            .name(ID_MAIN_VIEW)
            .margin(20.0)
            .placements(placements) // vector with placement strings
            .child(
                Grid::new()
                    .id(ID_GRID)
                    .rows("80, 200, 200, *")
                    .columns("200, 200, 250")
                    .child(
                        Button::new()
                            .id(ID_BUTTON)
                            .attach(Grid::row(0))
                            .attach(Grid::column(1))
                            .text("Click me to show the popup")
                            .on_click(move |state, _entity| -> bool {
                                state.send_message(MainViewAction::TogglePopup, id);
                                true
                            })
                            .build(ctx),
                    )
                    .child(
                        Grid::new()
                            .id(ID_GRID_PLACEMENT)
                            .attach(Grid::row(0))
                            .attach(Grid::column(2))
                            .rows("auto, 4, auto")
                            .columns("120, 8, auto")
                            //.style("container_form")
                            .name(ID_GRID_PLACEMENT)
                            .child(
                                TextBlock::new()
                                    .id(ID_PLACEMENT_COMBO_BOX_LABEL)
                                    .attach(Grid::row(0))
                                    .attach(Grid::column(0))
                                    .h_align("end")
                                    .text("Placement:")
                                    .v_align("center")
                                    .build(ctx),
                            )
                            .child(
                                ComboBox::new()
                                    .id(ID_PLACEMENT_COMBO_BOX)
                                    .attach(Grid::row(0))
                                    .attach(Grid::column(2))
                                    .count(count_placements)
                                    .style("popup_combo_box")
                                    .placement(Placement::Left)
                                    .items_builder(move |ibc, index| {
                                        let text = MainView::placements_ref(&ibc.get_widget(id))
                                            [index]
                                            .clone();
                                        TextBlock::new().v_align("center").text(text).build(ibc)
                                    })
                                    .on_changed("selected_index", move |states, _entity| {
                                        states.send_message(MainViewAction::UpdatePlacement, id);
                                    })
                                    .selected_index(id)
                                    .build(ctx),
                            )
                            .child(
                                TextBlock::new()
                                    .id(ID_PLACEMENT_OFFSET_LABEL)
                                    .attach(Grid::row(2))
                                    .attach(Grid::column(0))
                                    .h_align("end")
                                    .text("Offset:")
                                    .v_align("center")
                                    .build(ctx),
                            )
                            .child(
                                NumericBox::new()
                                    .id(ID_PLACEMENT_OFFSET)
                                    .attach(Grid::row(2))
                                    .attach(Grid::column(2))
                                    .style("popup_numeric_box")
                                    .max(50)
                                    .step(1)
                                    .val(5)
                                    //.water_mark("in pixel")
                                    .on_changed("val", move |states, _entity| {
                                        states.send_message(MainViewAction::UpdateOffset, id);
                                    })
                                    .build(ctx),
                            )
                            .build(ctx),
                    )
                    .child(target_container)
                    .build(ctx),
            )
    }
}

fn main() {
    // if no dictionary is set for the default language
    // the content of the text property will drawn.
    let localization = RonLocalization::create()
        .language("de_DE")
        //.language("en_US")
        .dictionary("de_DE", POPUP_DE_DE)
        .build();

    Application::new()
        .localization(localization)
        .theme(theme())
        .window(|ctx| {
            Window::new()
                .title("OrbTk - Popup example")
                .position((100.0, 100.0))
                .size(680, 690.0)
                .resizeable(true)
                .child(MainView::new().build(ctx))
                .build(ctx)
        })
        .run();
}
<commit_msg>🚨 Fix linter warnings<commit_after>use orbtk::{
    prelude::*,
    // only instantiate the `default` theme
    widgets::themes::theme_orbtk::{
        register_default_fonts, THEME_DEFAULT, THEME_DEFAULT_COLORS_DARK, THEME_DEFAULT_FONTS,
    },
};

// German localization file.
static POPUP_DE_DE: &str = include_str!("assets/popup/popup_de_DE.ron");

static DARK_EXT: &str = include_str!("assets/popup/default_dark.ron");

static ID_BUTTON: &str = "Toggle Popup";
static ID_GRID: &str = "PopupGrid";
static ID_GRID_PLACEMENT: &str = "Selection GRID";
static ID_MAIN_VIEW: &str = "MainView";
static ID_POPUP: &str = "Popup";
static ID_POPUP_STACK: &str = "PopupStack";
static ID_PLACEMENT_COMBO_BOX: &str = "Placement Selecton Box";
static ID_PLACEMENT_OFFSET: &str = "Placement Offset";
static ID_PLACEMENT_OFFSET_LABEL: &str = "Offset";
static ID_PLACEMENT_COMBO_BOX_LABEL: &str = "Placement Label";
static ID_TARGET: &str = "Target";

/// Valid `actions` that are handled as state changes in the `MainView` widget.
#[derive(Copy, Clone)]
enum MainViewAction {
    /// Toggle the visibility of the popup
    TogglePopup,

    /// Update the offset of the popup relative to the target
    UpdateOffset,

    /// Update the placement of the popup relative to the target
    UpdatePlacement,
}

/// Valid `structures` that are handled inside the state of the `MainView` widget.
#[derive(AsAny, Default)]
struct MainViewState {
    _action: Option<MainViewAction>,
}

/// Method definitions, that react on any given state change inside the `MainView` widget.
impl MainViewState {
    fn _action(&mut self, action: impl Into<Option<MainViewAction>>) {
        self._action = action.into();
    }

    fn toggle_popup(&mut self, ctx: &mut Context<'_>) {
        let open = ctx.child(ID_POPUP).clone::<bool>("open");

        if open {
            Popup::open_set(&mut ctx.child(ID_POPUP), false);
            Popup::visibility_set(&mut ctx.child(ID_POPUP), Visibility::Collapsed);
            Button::text_set(&mut ctx.child(ID_BUTTON), "Click me to show the popup");
        } else {
            Popup::visibility_set(&mut ctx.child(ID_POPUP), Visibility::Visible);
            Popup::open_set(&mut ctx.child(ID_POPUP), true);
            Button::text_set(&mut ctx.child(ID_BUTTON), "Click me to hide the popup");
        }
    }

    fn update_placement(&mut self, ctx: &mut Context<'_>) {
        let selected_index: i32 = ctx.child(ID_PLACEMENT_COMBO_BOX).clone("selected_index");
        let placement: Placement = ctx.child(ID_POPUP).clone_or_default("placement");
        match selected_index {
            0 => Popup::placement_set(&mut ctx.child(ID_POPUP), placement.bottom()),
            1 => Popup::placement_set(&mut ctx.child(ID_POPUP), placement.left()),
            2 => Popup::placement_set(&mut ctx.child(ID_POPUP), placement.right()),
            3 => Popup::placement_set(&mut ctx.child(ID_POPUP), placement.top()),
            _ => panic!(),
        }
        if cfg!(debug) {
            println!(
                "popup_v2: Placement {:?} (index: {:?}).",
                ctx.child(ID_POPUP).get::<Placement>("placement"),
                selected_index
            );
        }
    }

    fn update_offset(&mut self, ctx: &mut Context<'_>) {
        let offset: f64 = ctx.child(ID_PLACEMENT_OFFSET).clone("val");
        Popup::offset_set(&mut ctx.child(ID_POPUP), offset);

        if cfg!(debug) {
            println!(
                "popup_v2: Offset {:?}.",
                ctx.child(ID_POPUP).get::<f64>("offset")
            );
        }
    }
}

/// Associated methods handled inside the `MainViewState`
impl State for MainViewState {
    fn init(&mut self, _registry: &mut Registry, ctx: &mut Context) {
        // initialize the popup properties
        let offset: f64 = ctx.child(ID_PLACEMENT_OFFSET).clone("val");
        Popup::offset_set(&mut ctx.child(ID_POPUP), offset);
        Popup::open_set(&mut ctx.child(ID_POPUP), false);
        Popup::visibility_set(&mut ctx.child(ID_POPUP), Visibility::Collapsed);
    }

    fn messages(
        &mut self,
        mut messages: MessageReader,
        _registry: &mut Registry,
        ctx: &mut Context,
    ) {
        for message in messages.read::<MainViewAction>() {
            match message {
                MainViewAction::TogglePopup => {
                    MainViewState::toggle_popup(self, ctx);
                }
                MainViewAction::UpdatePlacement => {
                    MainViewState::update_placement(self, ctx);
                }
                MainViewAction::UpdateOffset => {
                    MainViewState::update_offset(self, ctx);
                }
            }
        }
    }
}

fn theme() -> Theme {
    register_default_fonts(Theme::from_config(
        ThemeConfig::from(DARK_EXT)
            .extend(ThemeConfig::from(THEME_DEFAULT))
            .extend(ThemeConfig::from(THEME_DEFAULT_COLORS_DARK))
            .extend(ThemeConfig::from(THEME_DEFAULT_FONTS)),
    ))
}

// Used to define ComboBox list members
type List = Vec<String>;

// constructs the MainView
widget!(
    MainView<MainViewState> {
    /// Active seleced index of combo box.
    selected_index: i32,

    /// Acitve selected position of popup.
    placements: List
    }
);

/// The template implementation of the main view
/// GUI elements are styled using the "style" attribute referencing to a ron based css
impl Template for MainView {
    fn template(self, id: Entity, ctx: &mut BuildContext) -> Self {
        let placements = vec![
            // TODO: make it sort independent
            "Bottom".to_string(),
            "Left".to_string(),
            "Right".to_string(),
            "Top".to_string(),
        ];
        let count_placements = placements.len();

        let target_container = Container::new()
            .id(ID_TARGET)
            .attach(Grid::row(2))
            .attach(Grid::column(1))
            .style("container_form")
            .child(
                Stack::new()
                    .orientation("vertical")
                    .spacing(16.0)
                    .child(
                        TextBlock::new()
                            .style("target_text_block")
                            .text("Target")
                            .build(ctx),
                    )
                    .child(
                        TextBlock::new()
                            .style("target_text_block")
                            .text("Target content")
                            .build(ctx),
                    )
                    .build(ctx),
            )
            .build(ctx);

        let popup = Popup::new()
            .id(ID_POPUP)
            .name(ID_POPUP)
            .style("popup_form")
            // alternative: popup is target of given coordinates (Point)
            //.target(Point::new(0.0, 0.0))
            .height(180.0)
            .open(false)
            .width(180.0)
            .target(target_container)
            .child(
                Container::new()
                    .child(
                        Stack::new()
                            .id(ID_POPUP_STACK)
                            .orientation("vertical")
                            .spacing(16.0)
                            .child(
                                TextBlock::new()
                                    .style("popup_text_block")
                                    .h_align("center")
                                    .v_align("center")
                                    .text("Popup Header")
                                    .build(ctx),
                            )
                            .child(
                                TextBlock::new()
                                    .style("popup_text_block")
                                    .h_align("center")
                                    .v_align("center")
                                    .text("Popup content")
                                    .build(ctx),
                            )
                            .build(ctx),
                    )
                    .build(ctx),
            )
            .build(ctx);

        let _ = ctx.append_child_to_overlay(popup);

        // Widget:  MainView
        self.id(ID_MAIN_VIEW)
            .name(ID_MAIN_VIEW)
            .margin(20.0)
            .placements(placements) // vector with placement strings
            .child(
                Grid::new()
                    .id(ID_GRID)
                    .rows("80, 200, 200, *")
                    .columns("200, 200, 250")
                    .child(
                        Button::new()
                            .id(ID_BUTTON)
                            .attach(Grid::row(0))
                            .attach(Grid::column(1))
                            .text("Click me to show the popup")
                            .on_click(move |state, _entity| -> bool {
                                state.send_message(MainViewAction::TogglePopup, id);
                                true
                            })
                            .build(ctx),
                    )
                    .child(
                        Grid::new()
                            .id(ID_GRID_PLACEMENT)
                            .attach(Grid::row(0))
                            .attach(Grid::column(2))
                            .rows("auto, 4, auto")
                            .columns("120, 8, auto")
                            //.style("container_form")
                            .name(ID_GRID_PLACEMENT)
                            .child(
                                TextBlock::new()
                                    .id(ID_PLACEMENT_COMBO_BOX_LABEL)
                                    .attach(Grid::row(0))
                                    .attach(Grid::column(0))
                                    .h_align("end")
                                    .text("Placement:")
                                    .v_align("center")
                                    .build(ctx),
                            )
                            .child(
                                ComboBox::new()
                                    .id(ID_PLACEMENT_COMBO_BOX)
                                    .attach(Grid::row(0))
                                    .attach(Grid::column(2))
                                    .count(count_placements)
                                    .style("popup_combo_box")
                                    .placement(Placement::Left)
                                    .items_builder(move |ibc, index| {
                                        let text = MainView::placements_ref(&ibc.get_widget(id))
                                            [index]
                                            .clone();
                                        TextBlock::new().v_align("center").text(text).build(ibc)
                                    })
                                    .on_changed("selected_index", move |states, _entity| {
                                        states.send_message(MainViewAction::UpdatePlacement, id);
                                    })
                                    .selected_index(id)
                                    .build(ctx),
                            )
                            .child(
                                TextBlock::new()
                                    .id(ID_PLACEMENT_OFFSET_LABEL)
                                    .attach(Grid::row(2))
                                    .attach(Grid::column(0))
                                    .h_align("end")
                                    .text("Offset:")
                                    .v_align("center")
                                    .build(ctx),
                            )
                            .child(
                                NumericBox::new()
                                    .id(ID_PLACEMENT_OFFSET)
                                    .attach(Grid::row(2))
                                    .attach(Grid::column(2))
                                    .style("popup_numeric_box")
                                    .max(50)
                                    .step(1)
                                    .val(5)
                                    //.water_mark("in pixel")
                                    .on_changed("val", move |states, _entity| {
                                        states.send_message(MainViewAction::UpdateOffset, id);
                                    })
                                    .build(ctx),
                            )
                            .build(ctx),
                    )
                    .child(target_container)
                    .build(ctx),
            )
    }
}

fn main() {
    // if no dictionary is set for the default language
    // the content of the text property will drawn.
    let localization = RonLocalization::create()
        .language("de_DE")
        //.language("en_US")
        .dictionary("de_DE", POPUP_DE_DE)
        .build();

    Application::new()
        .localization(localization)
        .theme(theme())
        .window(|ctx| {
            Window::new()
                .title("OrbTk - Popup example")
                .position((100.0, 100.0))
                .size(680, 690.0)
                .resizable(true)
                .child(MainView::new().build(ctx))
                .build(ctx)
        })
        .run();
}
<|endoftext|>


<commit_before>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// https://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! The implementations of the `Standard` distribution for other built-in types.

use core::char;
use core::num::Wrapping;

use {Rng};
use distributions::{Distribution, Standard, Uniform};

// ----- Sampling distributions -----

/// Sample a `char`, uniformly distributed over ASCII letters and numbers:
/// a-z, A-Z and 0-9.
///
/// # Example
///
/// ```
/// use std::iter;
/// use rand::{Rng, thread_rng};
/// use rand::distributions::Alphanumeric;
///
/// let mut rng = thread_rng();
/// let chars: String = iter::repeat(())
///         .map(|()| rng.sample(Alphanumeric))
///         .take(7)
///         .collect();
/// println!("Random chars: {}", chars);
/// ```
#[derive(Debug)]
pub struct Alphanumeric;

// ----- Implementations of distributions -----

impl Distribution<char> for Standard {
    #[inline]
    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> char {
        let range = Uniform::new(0u32, 0x11_0000);
        loop {
            match char::from_u32(range.sample(rng)) {
                Some(c) => return c,
                // About 0.2% of numbers in the range 0..0x110000 are invalid
                // codepoints (surrogates).
                None => {}
            }
        }
    }
}

impl Distribution<char> for Alphanumeric {
    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> char {
        const RANGE: u32 = 26 + 26 + 10;
        const GEN_ASCII_STR_CHARSET: &[u8] =
            b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
                abcdefghijklmnopqrstuvwxyz\
                0123456789";
        // We can pick from 62 characters. This is so close to a power of 2, 64,
        // that we can do better than `Uniform`. Use a simple bitshift and
        // rejection sampling. We do not use a bitmask, because for small RNGs
        // the most significant bits are usually of higher quality.
        loop {
            let var = rng.next_u32() >> (32 - 6);
            if var < RANGE {
                return GEN_ASCII_STR_CHARSET[var as usize] as char
            }
        }
    }
}

impl Distribution<bool> for Standard {
    #[inline]
    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> bool {
        // We can compare against an arbitrary bit of an u32 to get a bool.
        // Because the least significant bits of a lower quality RNG can have
        // simple patterns, we compare against the most significant bit. This is
        // easiest done using a sign test.
        (rng.next_u32() as i32) < 0
    }
}

macro_rules! tuple_impl {
    // use variables to indicate the arity of the tuple
    ($($tyvar:ident),* ) => {
        // the trailing commas are for the 1 tuple
        impl< $( $tyvar ),* >
            Distribution<( $( $tyvar ),* , )>
            for Standard
            where $( Standard: Distribution<$tyvar> ),*
        {
            #[inline]
            fn sample<R: Rng + ?Sized>(&self, _rng: &mut R) -> ( $( $tyvar ),* , ) {
                (
                    // use the $tyvar's to get the appropriate number of
                    // repeats (they're not actually needed)
                    $(
                        _rng.gen::<$tyvar>()
                    ),*
                    ,
                )
            }
        }
    }
}

impl Distribution<()> for Standard {
    #[inline]
    fn sample<R: Rng + ?Sized>(&self, _: &mut R) -> () { () }
}
tuple_impl!{A}
tuple_impl!{A, B}
tuple_impl!{A, B, C}
tuple_impl!{A, B, C, D}
tuple_impl!{A, B, C, D, E}
tuple_impl!{A, B, C, D, E, F}
tuple_impl!{A, B, C, D, E, F, G}
tuple_impl!{A, B, C, D, E, F, G, H}
tuple_impl!{A, B, C, D, E, F, G, H, I}
tuple_impl!{A, B, C, D, E, F, G, H, I, J}
tuple_impl!{A, B, C, D, E, F, G, H, I, J, K}
tuple_impl!{A, B, C, D, E, F, G, H, I, J, K, L}

macro_rules! array_impl {
    // recursive, given at least one type parameter:
    {$n:expr, $t:ident, $($ts:ident,)*} => {
        array_impl!{($n - 1), $($ts,)*}

        impl<T> Distribution<[T; $n]> for Standard where Standard: Distribution<T> {
            #[inline]
            fn sample<R: Rng + ?Sized>(&self, _rng: &mut R) -> [T; $n] {
                [_rng.gen::<$t>(), $(_rng.gen::<$ts>()),*]
            }
        }
    };
    // empty case:
    {$n:expr,} => {
        impl<T> Distribution<[T; $n]> for Standard {
            fn sample<R: Rng + ?Sized>(&self, _rng: &mut R) -> [T; $n] { [] }
        }
    };
}

array_impl!{32, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,}

impl<T> Distribution<Option<T>> for Standard where Standard: Distribution<T> {
    #[inline]
    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Option<T> {
        // UFCS is needed here: https://github.com/rust-lang/rust/issues/24066
        if rng.gen::<bool>() {
            Some(rng.gen())
        } else {
            None
        }
    }
}

impl<T> Distribution<Wrapping<T>> for Standard where Standard: Distribution<T> {
    #[inline]
    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Wrapping<T> {
        Wrapping(rng.gen())
    }
}

#[cfg(test)]
mod tests {
    use {Rng, RngCore, Standard};
    use distributions::Alphanumeric;
    #[cfg(all(not(feature="std"), feature="alloc"))] use alloc::String;

    #[test]
    fn test_misc() {
        let rng: &mut RngCore = &mut ::test::rng(820);

        rng.sample::<char, _>(Standard);
        rng.sample::<bool, _>(Standard);
    }

    #[cfg(feature="alloc")]
    #[test]
    fn test_chars() {
        use core::iter;
        let mut rng = ::test::rng(805);

        // Test by generating a relatively large number of chars, so we also
        // take the rejection sampling path.
        let word: String = iter::repeat(())
                .map(|()| rng.gen::<char>()).take(1000).collect();
        assert!(word.len() != 0);
    }

    #[test]
    fn test_alphanumeric() {
        let mut rng = ::test::rng(806);

        // Test by generating a relatively large number of chars, so we also
        // take the rejection sampling path.
        let mut incorrect = false;
        for _ in 0..100 {
            let c = rng.sample(Alphanumeric);
            incorrect |= !((c >= '0' && c <= '9') ||
                           (c >= 'A' && c <= 'Z') ||
                           (c >= 'a' && c <= 'z') );
        }
        assert!(incorrect == false);
    }
}
<commit_msg>Avoid surrogates when generating `char` using Standard distribution<commit_after>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// https://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! The implementations of the `Standard` distribution for other built-in types.

use core::char;
use core::num::Wrapping;

use {Rng};
use distributions::{Distribution, Standard, Uniform};

// ----- Sampling distributions -----

/// Sample a `char`, uniformly distributed over ASCII letters and numbers:
/// a-z, A-Z and 0-9.
///
/// # Example
///
/// ```
/// use std::iter;
/// use rand::{Rng, thread_rng};
/// use rand::distributions::Alphanumeric;
///
/// let mut rng = thread_rng();
/// let chars: String = iter::repeat(())
///         .map(|()| rng.sample(Alphanumeric))
///         .take(7)
///         .collect();
/// println!("Random chars: {}", chars);
/// ```
#[derive(Debug)]
pub struct Alphanumeric;

// ----- Implementations of distributions -----

impl Distribution<char> for Standard {
    #[inline]
    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> char {
        // A valid `char` is either in the interval `[0, 0xD800)` or
        // `(0xDFFF, 0x11_0000)`. All `char`s must therefore be in
        // `[0, 0x11_0000)` but not in the "gap" `[0xD800, 0xDFFF]` which is
        // reserved for surrogates. This is the size of that gap.
        const GAP_SIZE: u32 = 0xDFFF - 0xD800 + 1;

        let range = Uniform::new(GAP_SIZE, 0x11_0000);
        let mut n = range.sample(rng);
        if n <= 0xDFFF {
            n -= GAP_SIZE;
        }
        unsafe { char::from_u32_unchecked(n) }
    }
}

impl Distribution<char> for Alphanumeric {
    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> char {
        const RANGE: u32 = 26 + 26 + 10;
        const GEN_ASCII_STR_CHARSET: &[u8] =
            b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
                abcdefghijklmnopqrstuvwxyz\
                0123456789";
        // We can pick from 62 characters. This is so close to a power of 2, 64,
        // that we can do better than `Uniform`. Use a simple bitshift and
        // rejection sampling. We do not use a bitmask, because for small RNGs
        // the most significant bits are usually of higher quality.
        loop {
            let var = rng.next_u32() >> (32 - 6);
            if var < RANGE {
                return GEN_ASCII_STR_CHARSET[var as usize] as char
            }
        }
    }
}

impl Distribution<bool> for Standard {
    #[inline]
    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> bool {
        // We can compare against an arbitrary bit of an u32 to get a bool.
        // Because the least significant bits of a lower quality RNG can have
        // simple patterns, we compare against the most significant bit. This is
        // easiest done using a sign test.
        (rng.next_u32() as i32) < 0
    }
}

macro_rules! tuple_impl {
    // use variables to indicate the arity of the tuple
    ($($tyvar:ident),* ) => {
        // the trailing commas are for the 1 tuple
        impl< $( $tyvar ),* >
            Distribution<( $( $tyvar ),* , )>
            for Standard
            where $( Standard: Distribution<$tyvar> ),*
        {
            #[inline]
            fn sample<R: Rng + ?Sized>(&self, _rng: &mut R) -> ( $( $tyvar ),* , ) {
                (
                    // use the $tyvar's to get the appropriate number of
                    // repeats (they're not actually needed)
                    $(
                        _rng.gen::<$tyvar>()
                    ),*
                    ,
                )
            }
        }
    }
}

impl Distribution<()> for Standard {
    #[inline]
    fn sample<R: Rng + ?Sized>(&self, _: &mut R) -> () { () }
}
tuple_impl!{A}
tuple_impl!{A, B}
tuple_impl!{A, B, C}
tuple_impl!{A, B, C, D}
tuple_impl!{A, B, C, D, E}
tuple_impl!{A, B, C, D, E, F}
tuple_impl!{A, B, C, D, E, F, G}
tuple_impl!{A, B, C, D, E, F, G, H}
tuple_impl!{A, B, C, D, E, F, G, H, I}
tuple_impl!{A, B, C, D, E, F, G, H, I, J}
tuple_impl!{A, B, C, D, E, F, G, H, I, J, K}
tuple_impl!{A, B, C, D, E, F, G, H, I, J, K, L}

macro_rules! array_impl {
    // recursive, given at least one type parameter:
    {$n:expr, $t:ident, $($ts:ident,)*} => {
        array_impl!{($n - 1), $($ts,)*}

        impl<T> Distribution<[T; $n]> for Standard where Standard: Distribution<T> {
            #[inline]
            fn sample<R: Rng + ?Sized>(&self, _rng: &mut R) -> [T; $n] {
                [_rng.gen::<$t>(), $(_rng.gen::<$ts>()),*]
            }
        }
    };
    // empty case:
    {$n:expr,} => {
        impl<T> Distribution<[T; $n]> for Standard {
            fn sample<R: Rng + ?Sized>(&self, _rng: &mut R) -> [T; $n] { [] }
        }
    };
}

array_impl!{32, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,}

impl<T> Distribution<Option<T>> for Standard where Standard: Distribution<T> {
    #[inline]
    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Option<T> {
        // UFCS is needed here: https://github.com/rust-lang/rust/issues/24066
        if rng.gen::<bool>() {
            Some(rng.gen())
        } else {
            None
        }
    }
}

impl<T> Distribution<Wrapping<T>> for Standard where Standard: Distribution<T> {
    #[inline]
    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Wrapping<T> {
        Wrapping(rng.gen())
    }
}

#[cfg(test)]
mod tests {
    use {Rng, RngCore, Standard};
    use distributions::Alphanumeric;
    #[cfg(all(not(feature="std"), feature="alloc"))] use alloc::String;

    #[test]
    fn test_misc() {
        let rng: &mut RngCore = &mut ::test::rng(820);

        rng.sample::<char, _>(Standard);
        rng.sample::<bool, _>(Standard);
    }

    #[cfg(feature="alloc")]
    #[test]
    fn test_chars() {
        use core::iter;
        let mut rng = ::test::rng(805);

        // Test by generating a relatively large number of chars, so we also
        // take the rejection sampling path.
        let word: String = iter::repeat(())
                .map(|()| rng.gen::<char>()).take(1000).collect();
        assert!(word.len() != 0);
    }

    #[test]
    fn test_alphanumeric() {
        let mut rng = ::test::rng(806);

        // Test by generating a relatively large number of chars, so we also
        // take the rejection sampling path.
        let mut incorrect = false;
        for _ in 0..100 {
            let c = rng.sample(Alphanumeric);
            incorrect |= !((c >= '0' && c <= '9') ||
                           (c >= 'A' && c <= 'Z') ||
                           (c >= 'a' && c <= 'z') );
        }
        assert!(incorrect == false);
    }
}
<|endoftext|>


<commit_before>#[cfg(not(target_arch = "wasm32"))]
use std::{
    fs::{create_dir_all, File},
    io::Write,
};

#[cfg(not(target_arch = "wasm32"))]
use threadpool::*;

#[cfg(target_arch = "wasm32")]
use stdweb::web::window;

#[cfg(target_arch = "wasm32")]
use ron::de::from_str;

#[cfg(not(target_arch = "wasm32"))]
use ron::de::from_reader;

use ron::ser::{to_string_pretty, PrettyConfig};

use serde::{de::DeserializeOwned, Serialize};

use dces::entity::Entity;

use crate::widget_base::MessageAdapter;

#[derive(Debug)]
pub enum SettingsError {
    Saved(String),
    Loaded(String),
}

pub type SettingsResult<T> = Result<T, SettingsError>;

/// `Settings` represents a global settings service that could be use
/// to serialize and deserialize data in the `ron` file
/// format. Settings are stored in the user settings directory
/// (depending on the operating system) under the a folder with the
/// given application name.
#[derive(Debug, Clone)]
pub struct Settings {
    app_name: String,
    message_adapter: MessageAdapter,

    #[cfg(not(target_arch = "wasm32"))]
    pool: ThreadPool,
}

impl Settings {
    /// Creates a new `Settings` service with an default name.
    pub fn new(message_adapter: MessageAdapter) -> Self {
        Settings {
            app_name: "orbtk_app".into(),
            message_adapter,

            #[cfg(not(target_arch = "wasm32"))]
            pool: ThreadPool::new(4),
        }
    }
    /// Creates a new `Settings` service with the given app name.
    pub fn from_name(app_name: String, message_adapter: MessageAdapter) -> Self {
        Settings {
            app_name,
            message_adapter,

            #[cfg(not(target_arch = "wasm32"))]
            pool: ThreadPool::new(4),
        }
    }

    /// Gets the app name of the setting service.
    pub fn app_name(&self) -> &str {
        &*self.app_name
    }

    /// Serialize the given data object from user's config dir. Sends
    /// the result `Result<(), String>` as message to the given
    /// entity.
    #[cfg(not(target_arch = "wasm32"))]
    pub fn save_async<S: Serialize + Send + Sync + 'static>(
        &self,
        key: String,
        data: S,
        entity: Entity,
    ) {
        let app_name = self.app_name.clone();
        let message_adapter = self.message_adapter.clone();

        self.pool.execute(move || {
            message_adapter.send_message(save(app_name.as_str(), key.as_str(), &data), entity);
        })
    }

    /// Loads and deserialize data from user's config dir. Send the
    /// result `Result<D, String>` as message to the given entity.
    #[cfg(not(target_arch = "wasm32"))]
    pub fn load_async<D: DeserializeOwned + Send + Sync + 'static>(
        &self,
        key: String,
        entity: Entity,
    ) {
        let app_name = self.app_name.clone();
        let message_adapter = self.message_adapter.clone();

        self.pool.execute(move || {
            message_adapter.send_message(load::<D>(app_name.as_str(), key.as_str()), entity);
        })
    }

    #[cfg(target_arch = "wasm32")]
    pub fn save_async<S: Serialize + Send + Sync + 'static>(
        &self,
        key: String,
        data: S,
        entity: Entity,
    ) {
        self.message_adapter
            .send_message(save(self.app_name.as_str(), key.as_str(), &data), entity);
    }

    /// Loads and deserialize data from user's config dir. Send the
    /// result `Result<D, String>` as message to the given entity.
    #[cfg(target_arch = "wasm32")]
    pub fn load_async<D: DeserializeOwned + Send + Sync + 'static>(
        &self,
        key: String,
        entity: Entity,
    ) {
        self.message_adapter
            .send_message(load::<D>(self.app_name.as_str(), key.as_str()), entity);
    }

    /// Serialize the given data object from user's config dir.
    pub fn save<S: Serialize>(&self, key: &str, data: &S) -> SettingsResult<()> {
        save(self.app_name.as_str(), key, data)
    }

    /// Loads and deserialize data from user's config dir.
    pub fn load<D: DeserializeOwned>(&self, key: &str) -> SettingsResult<D> {
        load(self.app_name.as_str(), key)
    }
}

// --- Helper --

#[cfg(not(target_arch = "wasm32"))]
fn save<S: Serialize>(app_name: &str, key: &str, data: &S) -> SettingsResult<()> {
    let content = to_string_pretty(data, PrettyConfig::default());

    if let Some(config_path) = &mut dirs_next::config_dir() {
        config_path.push(app_name);

        if !config_path.exists() {
            let result = create_dir_all(&config_path);

            if result.is_err() {
                return Err(SettingsError::Saved(format!(
                    "Settings.save: Could not create settings dir {:?}",
                    config_path
                )));
            }
        }

        config_path.push(format!("{}.ron", key));

        if let Ok(file) = &mut File::create(&config_path) {
            let result = file.write_all(content.unwrap().as_bytes());
            if result.is_err() {
                return Err(SettingsError::Saved(format!(
                    "Settings.save: Could not write to config file {:?}",
                    config_path
                )));
            }
        } else {
            return Err(SettingsError::Saved(format!(
                "Settings.save: Could not create config file {:?}",
                config_path
            )));
        }
    }

    Ok(())
}

#[cfg(not(target_arch = "wasm32"))]
fn load<D: DeserializeOwned>(app_name: &str, key: &str) -> SettingsResult<D> {
    if let Some(config_path) = &mut dirs_next::config_dir() {
        config_path.push(app_name);
        config_path.push(format!("{}.ron", key));

        if let Ok(file) = &mut File::open(&config_path) {
            if let Ok(data) = from_reader(file) {
                return Ok(data);
            } else {
                return Err(SettingsError::Loaded(format!(
                    "Settings.load: Could not read data from config file {:?}",
                    config_path
                )));
            }
        } else {
            return Err(SettingsError::Loaded(format!(
                "Settings.load: Could not open config file {:?}",
                config_path
            )));
        }
    }

    Err(SettingsError::Loaded(format!(
        "Settings.load: Could not load settings with key: {}",
        key
    )))
}

#[cfg(target_arch = "wasm32")]
fn save<S: Serialize>(_app_name: &str, key: &str, data: &S) -> SettingsResult<()> {
    let content = to_string_pretty(data, PrettyConfig::default());
    if window()
        .local_storage()
        .insert(key, content.unwrap().as_str())
        .is_ok()
    {
        return Ok(());
    }

    Err(SettingsError::Saved(format!(
        "Settings.save: Could not write settings with key {} to local browser storage.",
        key
    )))
}

#[cfg(target_arch = "wasm32")]
fn load<D: DeserializeOwned>(_app_name: &str, key: &str) -> SettingsResult<D> {
    if let Some(data) = window().local_storage().get(key) {
        if let Ok(data) = from_str(data.as_str()) {
            return Ok(data);
        } else {
            return Err(SettingsError::Loaded(format!(
                "Settings.load: Could not read data from local browser storage with key: {}",
                key
            )));
        }
    }

    Err(SettingsError::Loaded(format!(
        "Settings.load: Could not read data from local browser storage with key: {}",
        key
    )))
}

// --- Helper --
<commit_msg>orbtk_core: update service/settings documentation<commit_after>#[cfg(not(target_arch = "wasm32"))]
use std::{
    fs::{create_dir_all, File},
    io::Write,
};

#[cfg(not(target_arch = "wasm32"))]
use threadpool::*;

#[cfg(target_arch = "wasm32")]
use stdweb::web::window;

#[cfg(target_arch = "wasm32")]
use ron::de::from_str;

#[cfg(not(target_arch = "wasm32"))]
use ron::de::from_reader;

use ron::ser::{to_string_pretty, PrettyConfig};

use serde::{de::DeserializeOwned, Serialize};

use dces::entity::Entity;

use crate::widget_base::MessageAdapter;

/// Used to report meaningful error messages when handling `Settings`.
#[derive(Debug)]
pub enum SettingsError {
    Saved(String),
    Loaded(String),
}

/// Returns a value or the error message.
pub type SettingsResult<T> = Result<T, SettingsError>;

/// `Settings` represents a global settings service that could be use
/// to serialize and deserialize data in the `ron` file
/// format. Settings are stored in the user settings directory
/// (depending on the operating system) under the a folder with the
/// given application name.
#[derive(Debug, Clone)]
pub struct Settings {
    app_name: String,
    message_adapter: MessageAdapter,

    #[cfg(not(target_arch = "wasm32"))]
    pool: ThreadPool,
}

impl Settings {
    /// Creates a new `Settings` service with an default name.
    pub fn new(message_adapter: MessageAdapter) -> Self {
        Settings {
            app_name: "orbtk_app".into(),
            message_adapter,

            #[cfg(not(target_arch = "wasm32"))]
            pool: ThreadPool::new(4),
        }
    }
    /// Creates a new `Settings` service with the given app name.
    pub fn from_name(app_name: String, message_adapter: MessageAdapter) -> Self {
        Settings {
            app_name,
            message_adapter,

            #[cfg(not(target_arch = "wasm32"))]
            pool: ThreadPool::new(4),
        }
    }

    /// Gets the app name of the setting service.
    pub fn app_name(&self) -> &str {
        &*self.app_name
    }

    /// Serialize the given data object from user's config dir. Sends
    /// the result `Result<(), String>` as message to the given
    /// entity.
    #[cfg(not(target_arch = "wasm32"))]
    pub fn save_async<S: Serialize + Send + Sync + 'static>(
        &self,
        key: String,
        data: S,
        entity: Entity,
    ) {
        let app_name = self.app_name.clone();
        let message_adapter = self.message_adapter.clone();

        self.pool.execute(move || {
            message_adapter.send_message(save(app_name.as_str(), key.as_str(), &data), entity);
        })
    }

    /// Loads and deserialize data from user's config dir. Send the
    /// result `Result<D, String>` as message to the given entity.
    #[cfg(not(target_arch = "wasm32"))]
    pub fn load_async<D: DeserializeOwned + Send + Sync + 'static>(
        &self,
        key: String,
        entity: Entity,
    ) {
        let app_name = self.app_name.clone();
        let message_adapter = self.message_adapter.clone();

        self.pool.execute(move || {
            message_adapter.send_message(load::<D>(app_name.as_str(), key.as_str()), entity);
        })
    }

    #[cfg(target_arch = "wasm32")]
    pub fn save_async<S: Serialize + Send + Sync + 'static>(
        &self,
        key: String,
        data: S,
        entity: Entity,
    ) {
        self.message_adapter
            .send_message(save(self.app_name.as_str(), key.as_str(), &data), entity);
    }

    /// Loads and deserialize data from user's config dir. Send the
    /// result `Result<D, String>` as message to the given entity.
    #[cfg(target_arch = "wasm32")]
    pub fn load_async<D: DeserializeOwned + Send + Sync + 'static>(
        &self,
        key: String,
        entity: Entity,
    ) {
        self.message_adapter
            .send_message(load::<D>(self.app_name.as_str(), key.as_str()), entity);
    }

    /// Serialize the given data object from user's config dir.
    pub fn save<S: Serialize>(&self, key: &str, data: &S) -> SettingsResult<()> {
        save(self.app_name.as_str(), key, data)
    }

    /// Loads and deserialize data from user's config dir.
    pub fn load<D: DeserializeOwned>(&self, key: &str) -> SettingsResult<D> {
        load(self.app_name.as_str(), key)
    }
}

// --- Helper --

#[cfg(not(target_arch = "wasm32"))]
fn save<S: Serialize>(app_name: &str, key: &str, data: &S) -> SettingsResult<()> {
    let content = to_string_pretty(data, PrettyConfig::default());

    if let Some(config_path) = &mut dirs_next::config_dir() {
        config_path.push(app_name);

        if !config_path.exists() {
            let result = create_dir_all(&config_path);

            if result.is_err() {
                return Err(SettingsError::Saved(format!(
                    "Settings.save: Could not create settings dir {:?}",
                    config_path
                )));
            }
        }

        config_path.push(format!("{}.ron", key));

        if let Ok(file) = &mut File::create(&config_path) {
            let result = file.write_all(content.unwrap().as_bytes());
            if result.is_err() {
                return Err(SettingsError::Saved(format!(
                    "Settings.save: Could not write to config file {:?}",
                    config_path
                )));
            }
        } else {
            return Err(SettingsError::Saved(format!(
                "Settings.save: Could not create config file {:?}",
                config_path
            )));
        }
    }

    Ok(())
}

#[cfg(not(target_arch = "wasm32"))]
fn load<D: DeserializeOwned>(app_name: &str, key: &str) -> SettingsResult<D> {
    if let Some(config_path) = &mut dirs_next::config_dir() {
        config_path.push(app_name);
        config_path.push(format!("{}.ron", key));

        if let Ok(file) = &mut File::open(&config_path) {
            if let Ok(data) = from_reader(file) {
                return Ok(data);
            } else {
                return Err(SettingsError::Loaded(format!(
                    "Settings.load: Could not read data from config file {:?}",
                    config_path
                )));
            }
        } else {
            return Err(SettingsError::Loaded(format!(
                "Settings.load: Could not open config file {:?}",
                config_path
            )));
        }
    }

    Err(SettingsError::Loaded(format!(
        "Settings.load: Could not load settings with key: {}",
        key
    )))
}

#[cfg(target_arch = "wasm32")]
fn save<S: Serialize>(_app_name: &str, key: &str, data: &S) -> SettingsResult<()> {
    let content = to_string_pretty(data, PrettyConfig::default());
    if window()
        .local_storage()
        .insert(key, content.unwrap().as_str())
        .is_ok()
    {
        return Ok(());
    }

    Err(SettingsError::Saved(format!(
        "Settings.save: Could not write settings with key {} to local browser storage.",
        key
    )))
}

#[cfg(target_arch = "wasm32")]
fn load<D: DeserializeOwned>(_app_name: &str, key: &str) -> SettingsResult<D> {
    if let Some(data) = window().local_storage().get(key) {
        if let Ok(data) = from_str(data.as_str()) {
            return Ok(data);
        } else {
            return Err(SettingsError::Loaded(format!(
                "Settings.load: Could not read data from local browser storage with key: {}",
                key
            )));
        }
    }

    Err(SettingsError::Loaded(format!(
        "Settings.load: Could not read data from local browser storage with key: {}",
        key
    )))
}

// --- Helper --
<|endoftext|>


<commit_before>//! [POST /_matrix/client/r0/room_keys/version](https://matrix.org/docs/spec/client_server/unstable#post-matrix-client-r0-room-keys-version)

use ruma_api::ruma_api;
use ruma_serde::Raw;

use super::BackupAlgorithm;

ruma_api! {
    metadata: {
        description: "Update information about an existing backup.",
        method: POST,
        name: "update_backup",
        path: "/_matrix/client/r0/room_keys/version/:version",
        rate_limited: true,
        authentication: AccessToken,
    }

    request: {
        /// The backup version.
        #[ruma_api(path)]
        pub version: &'a str,

        /// The algorithm used for storing backups.
        #[serde(flatten)]
        pub algorithm: Raw<BackupAlgorithm>,
    }

    #[derive(Default)]
    response: {}

    error: crate::Error
}

impl<'a> Request<'a> {
    /// Creates a new `Request` with the given backup version and algorithm.
    pub fn new(version: &'a str, algorithm: Raw<BackupAlgorithm>) -> Self {
        Self { version, algorithm }
    }
}

impl Response {
    /// Creates an empty `Response`.
    pub fn new() -> Self {
        Self {}
    }
}
<commit_msg>client-api: Fix broken update_backup::Request deserialization<commit_after>//! [POST /_matrix/client/r0/room_keys/version](https://matrix.org/docs/spec/client_server/unstable#post-matrix-client-r0-room-keys-version)

use ruma_api::ruma_api;
use ruma_serde::Raw;

use super::BackupAlgorithm;

ruma_api! {
    metadata: {
        description: "Update information about an existing backup.",
        method: POST,
        name: "update_backup",
        path: "/_matrix/client/r0/room_keys/version/:version",
        rate_limited: true,
        authentication: AccessToken,
    }

    request: {
        /// The backup version.
        #[ruma_api(path)]
        pub version: &'a str,

        /// The algorithm used for storing backups.
        #[ruma_api(body)]
        pub algorithm: Raw<BackupAlgorithm>,
    }

    #[derive(Default)]
    response: {}

    error: crate::Error
}

impl<'a> Request<'a> {
    /// Creates a new `Request` with the given backup version and algorithm.
    pub fn new(version: &'a str, algorithm: Raw<BackupAlgorithm>) -> Self {
        Self { version, algorithm }
    }
}

impl Response {
    /// Creates an empty `Response`.
    pub fn new() -> Self {
        Self {}
    }
}
<|endoftext|>


<commit_before>//! Provides an animated color value, so you can have colors change over time

use std::cmp::{Eq, Ord, PartialOrd, PartialEq, Ordering};

use linalg;
use film::Colorf;

/// ColorKeyframe is a color associated with a specific time
#[derive(Debug, Copy, Clone)]
pub struct ColorKeyframe {
    pub color: Colorf,
    pub time: f32,
}

impl ColorKeyframe {
    pub fn new(color: &Colorf, time: f32) -> ColorKeyframe {
        ColorKeyframe { color: *color, time: time }
    }
}
impl Ord for ColorKeyframe {
    fn cmp(&self, other: &ColorKeyframe) -> Ordering {
        self.partial_cmp(other).unwrap()
    }
}
impl PartialOrd for ColorKeyframe {
    fn partial_cmp(&self, other: &ColorKeyframe) -> Option<Ordering> {
        self.time.partial_cmp(&other.time)
    }
}
impl Eq for ColorKeyframe {}
impl PartialEq for ColorKeyframe {
    fn eq(&self, other: &ColorKeyframe) -> bool {
        self.time == other.time
    }
}

/// AnimatedColor is a list of colors associated with time points in the scene
/// that will compute the color at the desired time by blending the two nearest ones
#[derive(Debug, Clone)]
pub struct AnimatedColor {
    /// List of color keyframes in time order
    keyframes: Vec<ColorKeyframe>,
}

impl AnimatedColor {
    /// Create an animated transform that will blend between the passed keyframes
    pub fn with_keyframes(mut keyframes: Vec<ColorKeyframe>) -> AnimatedColor {
        keyframes.sort();
        AnimatedColor { keyframes: keyframes }
    }
    /// Compute the color at the desired time
    pub fn color(&self, time: f32) -> Colorf {
        if self.keyframes.is_empty() {
            Colorf::black()
        } else if self.keyframes.len() == 1 {
            self.keyframes[0].color
        } else {
            // TODO: Binary search here somehow? Or does the BTreeSet have some faster impl
            // of take/skip while?
            let first = self.keyframes.iter().take_while(|k| k.time < time).last();
            let second = self.keyframes.iter().skip_while(|k| k.time < time).next();
            if first.is_none() {
                self.keyframes.first().unwrap().color
            } else if second.is_none() {
                self.keyframes.last().unwrap().color
            } else {
                let mut color = Colorf::black();
                let f = first.unwrap().color;
                let s = second.unwrap().color;
                color.r = linalg::lerp(time, &f.r, &s.r);
                color.g = linalg::lerp(time, &f.g, &s.g);
                color.b = linalg::lerp(time, &f.b, &s.b);
                color.a = linalg::lerp(time, &f.a, &s.a);
                color.clamp()
            }
        }
    }
}

<commit_msg>no that's actually right, didn't think carefully enough<commit_after>//! Provides an animated color value, so you can have colors change over time

use std::cmp::{Eq, Ord, PartialOrd, PartialEq, Ordering};

use linalg;
use film::Colorf;

/// ColorKeyframe is a color associated with a specific time
#[derive(Debug, Copy, Clone)]
pub struct ColorKeyframe {
    pub color: Colorf,
    pub time: f32,
}

impl ColorKeyframe {
    pub fn new(color: &Colorf, time: f32) -> ColorKeyframe {
        ColorKeyframe { color: *color, time: time }
    }
}
impl Ord for ColorKeyframe {
    fn cmp(&self, other: &ColorKeyframe) -> Ordering {
        self.partial_cmp(other).unwrap()
    }
}
impl PartialOrd for ColorKeyframe {
    fn partial_cmp(&self, other: &ColorKeyframe) -> Option<Ordering> {
        self.time.partial_cmp(&other.time)
    }
}
impl Eq for ColorKeyframe {}
impl PartialEq for ColorKeyframe {
    fn eq(&self, other: &ColorKeyframe) -> bool {
        self.time == other.time
    }
}

/// AnimatedColor is a list of colors associated with time points in the scene
/// that will compute the color at the desired time by blending the two nearest ones
#[derive(Debug, Clone)]
pub struct AnimatedColor {
    /// List of color keyframes in time order
    keyframes: Vec<ColorKeyframe>,
}

impl AnimatedColor {
    /// Create an animated transform that will blend between the passed keyframes
    pub fn with_keyframes(mut keyframes: Vec<ColorKeyframe>) -> AnimatedColor {
        keyframes.sort();
        AnimatedColor { keyframes: keyframes }
    }
    /// Compute the color at the desired time
    pub fn color(&self, time: f32) -> Colorf {
        if self.keyframes.is_empty() {
            Colorf::black()
        } else if self.keyframes.len() == 1 {
            self.keyframes[0].color
        } else {
            // TODO: Binary search here somehow? Or does the BTreeSet have some faster impl
            // of take/skip while?
            let first = self.keyframes.iter().take_while(|k| k.time < time).last();
            let second = self.keyframes.iter().skip_while(|k| k.time < time).next();
            if first.is_none() {
                self.keyframes.first().unwrap().color
            } else if second.is_none() {
                self.keyframes.last().unwrap().color
            } else {
                let mut color = Colorf::black();
                let f = first.unwrap().color;
                let s = second.unwrap().color;
                color.r = linalg::lerp(time, &f.r, &s.r);
                color.g = linalg::lerp(time, &f.g, &s.g);
                color.b = linalg::lerp(time, &f.b, &s.b);
                color.a = linalg::lerp(time, &f.a, &s.a);
                color
            }
        }
    }
}

<|endoftext|>


<commit_before>use neb::server::*;
use neb::ram::schema::LocalSchemasCache;
use bifrost::rpc;
use bifrost::raft;
use bifrost::membership::server::Membership;
use bifrost::raft::client::RaftClient;
use bifrost::membership::member::MemberService;
use bifrost::raft::state_machine::{master as sm_master};
use bifrost::rpc::Server;
use std::rc::Rc;
use std::sync::Arc;
use futures::prelude::*;
use std::env;
use neb::ram::schema::Schema;
use neb::ram::schema::Field;
use dovahkiin::types::type_id_of;
use neb::client;
use neb::ram::types::*;
use dovahkiin::types::custom_types::id::Id;
use dovahkiin::types::custom_types::map::Map;
use neb::ram::cell::Cell;

#[test]
pub fn init () {
    NebServer::new_from_opts(&ServerOptions {
        chunk_count: 1,
        memory_size: 16 * 1024,
        backup_storage: None,
        wal_storage: None
    },
    &String::from("127.0.0.1:5100"),
    &String::from("test"));
}

#[test]
pub fn smoke_test () {
    const DATA: &'static str = "DATA";
    let num = env::var("NEB_KV_SMOKE_TEST_ITEMS")
        .unwrap_or("1000".to_string())
        .parse::<u64>().unwrap();
    env_logger::init();
    let server_addr = String::from("127.0.0.1:5300");
    let server_group = String::from("smoke_test");
    let server = NebServer::new_from_opts(&ServerOptions {
        chunk_count: 1,
        memory_size: 512 * 1024 * 1024,
        backup_storage: None,
        wal_storage: None
    },
    &server_addr,
    &server_group);
    let schema_id = 123;
    let schema = Schema {
        id: schema_id,
        name: String::from("schema"),
        key_field: None,
        str_key_field: None,
        is_dynamic: false,
        fields: Field::new(
            "*", 0, false, false,
            Some(vec![
                Field::new(DATA, type_id_of(Type::U64), false, false, None)
            ])
        )
    };

    let client = Arc::new(client::AsyncClient::new(
        &server.rpc, &vec!(server_addr),
        &server_group).unwrap());
    client.new_schema_with_id(schema).wait();

    for i in 0..num * 2 {
        // intense upsert, half delete
        let id = Id::new(0, num / 2);
        let mut value = Value::Map(Map::new());
        value[DATA] = Value::U64(num);
        let cell = Cell::new_with_id(schema_id, &id, value);
        client.upsert_cell(cell).wait();

        // verify
        let read_cell = client.read_cell(id).wait().unwrap().unwrap();
        assert_eq!(*(read_cell.data[DATA].U64().unwrap()), num);

        if i % 2 == 0 {
            client.remove_cell(id).wait();
        }
    }
}<commit_msg>regression<commit_after>use neb::server::*;
use neb::ram::schema::LocalSchemasCache;
use bifrost::rpc;
use bifrost::raft;
use bifrost::membership::server::Membership;
use bifrost::raft::client::RaftClient;
use bifrost::membership::member::MemberService;
use bifrost::raft::state_machine::{master as sm_master};
use bifrost::rpc::Server;
use std::rc::Rc;
use std::sync::Arc;
use futures::prelude::*;
use std::env;
use neb::ram::schema::Schema;
use neb::ram::schema::Field;
use dovahkiin::types::type_id_of;
use neb::client;
use neb::ram::types::*;
use dovahkiin::types::custom_types::id::Id;
use dovahkiin::types::custom_types::map::Map;
use neb::ram::cell::Cell;

#[test]
pub fn init () {
    NebServer::new_from_opts(&ServerOptions {
        chunk_count: 1,
        memory_size: 16 * 1024,
        backup_storage: None,
        wal_storage: None
    },
    &String::from("127.0.0.1:5100"),
    &String::from("test"));
}

#[test]
pub fn smoke_test () {
    const DATA: &'static str = "DATA";
    let num = env::var("NEB_KV_SMOKE_TEST_ITEMS")
        .unwrap_or("1000".to_string())
        .parse::<u64>().unwrap();
    env_logger::init();
    let server_addr = String::from("127.0.0.1:5300");
    let server_group = String::from("smoke_test");
    let server = NebServer::new_from_opts(&ServerOptions {
        chunk_count: 1,
        memory_size: 512 * 1024 * 1024,
        backup_storage: None,
        wal_storage: None
    },
    &server_addr,
    &server_group);
    let schema_id = 123;
    let schema = Schema {
        id: schema_id,
        name: String::from("schema"),
        key_field: None,
        str_key_field: None,
        is_dynamic: false,
        fields: Field::new(
            "*", 0, false, false,
            Some(vec![
                Field::new(DATA, type_id_of(Type::U64), false, false, None)
            ])
        )
    };

    let client = Arc::new(client::AsyncClient::new(
        &server.rpc, &vec!(server_addr),
        &server_group).unwrap());
    client.new_schema_with_id(schema).wait();

    for i in 0..num * 2 {
        // intense upsert, half delete
        let id = Id::new(0, num / 2);
        let mut value = Value::Map(Map::new());
        value[DATA] = Value::U64(num);
        let cell = Cell::new_with_id(schema_id, &id, value);
        client.upsert_cell(cell).wait();

        // verify
        let read_cell = client.read_cell(id).wait().unwrap().unwrap();
        assert_eq!(*(read_cell.data[DATA].U64().unwrap()), num);

        if i % 2 == 0 {
            client.remove_cell(id).wait();
        }
    }

    for i in 0..num {
        let id = Id::new(0, num);
        let mut value = Value::Map(Map::new());
        value[DATA] = Value::U64(num * 2);
        let cell = Cell::new_with_id(schema_id, &id, value);
        client.upsert_cell(cell).wait();

        // verify
        let read_cell = client.read_cell(id).wait().unwrap().unwrap();
        assert_eq!(*(read_cell.data[DATA].U64().unwrap()), num * 2);
    }
}<|endoftext|>