use std::error::Error;
use std::fmt;
use std::io;
#[allow(warnings)]
use futures::future;
use futures::Future;
use rusoto_core::region;
use rusoto_core::request::{BufferedHttpResponse, DispatchSignedRequest};
use rusoto_core::{Client, RusotoFuture};
use rusoto_core::credential::{CredentialsError, ProvideAwsCredentials};
use rusoto_core::request::HttpDispatchError;
use rusoto_core::signature::SignedRequest;
use serde_json;
use serde_json::from_slice;
use serde_json::Value as SerdeJsonValue;
#[derive(Default, Debug, Clone, PartialEq, Serialize)]
pub struct BatchMeterUsageRequest {
#[serde(rename = "ProductCode")]
pub product_code: String,
#[serde(rename = "UsageRecords")]
pub usage_records: Vec<UsageRecord>,
}
#[derive(Default, Debug, Clone, PartialEq, Deserialize)]
#[cfg_attr(test, derive(Serialize))]
pub struct BatchMeterUsageResult {
#[serde(rename = "Results")]
#[serde(skip_serializing_if = "Option::is_none")]
pub results: Option<Vec<UsageRecordResult>>,
#[serde(rename = "UnprocessedRecords")]
#[serde(skip_serializing_if = "Option::is_none")]
pub unprocessed_records: Option<Vec<UsageRecord>>,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize)]
pub struct MeterUsageRequest {
#[serde(rename = "DryRun")]
pub dry_run: bool,
#[serde(rename = "ProductCode")]
pub product_code: String,
#[serde(rename = "Timestamp")]
pub timestamp: f64,
#[serde(rename = "UsageDimension")]
pub usage_dimension: String,
#[serde(rename = "UsageQuantity")]
pub usage_quantity: i64,
}
#[derive(Default, Debug, Clone, PartialEq, Deserialize)]
#[cfg_attr(test, derive(Serialize))]
pub struct MeterUsageResult {
#[serde(rename = "MeteringRecordId")]
#[serde(skip_serializing_if = "Option::is_none")]
pub metering_record_id: Option<String>,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize)]
pub struct RegisterUsageRequest {
#[serde(rename = "Nonce")]
#[serde(skip_serializing_if = "Option::is_none")]
pub nonce: Option<String>,
#[serde(rename = "ProductCode")]
pub product_code: String,
#[serde(rename = "PublicKeyVersion")]
pub public_key_version: i64,
}
#[derive(Default, Debug, Clone, PartialEq, Deserialize)]
#[cfg_attr(test, derive(Serialize))]
pub struct RegisterUsageResult {
#[serde(rename = "PublicKeyRotationTimestamp")]
#[serde(skip_serializing_if = "Option::is_none")]
pub public_key_rotation_timestamp: Option<f64>,
#[serde(rename = "Signature")]
#[serde(skip_serializing_if = "Option::is_none")]
pub signature: Option<String>,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize)]
pub struct ResolveCustomerRequest {
#[serde(rename = "RegistrationToken")]
pub registration_token: String,
}
#[derive(Default, Debug, Clone, PartialEq, Deserialize)]
#[cfg_attr(test, derive(Serialize))]
pub struct ResolveCustomerResult {
#[serde(rename = "CustomerIdentifier")]
#[serde(skip_serializing_if = "Option::is_none")]
pub customer_identifier: Option<String>,
#[serde(rename = "ProductCode")]
#[serde(skip_serializing_if = "Option::is_none")]
pub product_code: Option<String>,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct UsageRecord {
#[serde(rename = "CustomerIdentifier")]
pub customer_identifier: String,
#[serde(rename = "Dimension")]
pub dimension: String,
#[serde(rename = "Quantity")]
pub quantity: i64,
#[serde(rename = "Timestamp")]
pub timestamp: f64,
}
#[derive(Default, Debug, Clone, PartialEq, Deserialize)]
#[cfg_attr(test, derive(Serialize))]
pub struct UsageRecordResult {
#[serde(rename = "MeteringRecordId")]
#[serde(skip_serializing_if = "Option::is_none")]
pub metering_record_id: Option<String>,
#[serde(rename = "Status")]
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<String>,
#[serde(rename = "UsageRecord")]
#[serde(skip_serializing_if = "Option::is_none")]
pub usage_record: Option<UsageRecord>,
}
#[derive(Debug, PartialEq)]
pub enum BatchMeterUsageError {
DisabledApi(String),
InternalServiceError(String),
InvalidCustomerIdentifier(String),
InvalidProductCode(String),
InvalidUsageDimension(String),
Throttling(String),
TimestampOutOfBounds(String),
HttpDispatch(HttpDispatchError),
Credentials(CredentialsError),
Validation(String),
ParseError(String),
Unknown(BufferedHttpResponse),
}
impl BatchMeterUsageError {
pub fn from_response(res: BufferedHttpResponse) -> BatchMeterUsageError {
if let Ok(json) = from_slice::<SerdeJsonValue>(&res.body) {
let raw_error_type = json
.get("__type")
.and_then(|e| e.as_str())
.unwrap_or("Unknown");
let error_message = json.get("message").and_then(|m| m.as_str()).unwrap_or("");
let pieces: Vec<&str> = raw_error_type.split("#").collect();
let error_type = pieces.last().expect("Expected error type");
match *error_type {
"DisabledApiException" => {
return BatchMeterUsageError::DisabledApi(String::from(error_message));
}
"InternalServiceErrorException" => {
return BatchMeterUsageError::InternalServiceError(String::from(error_message));
}
"InvalidCustomerIdentifierException" => {
return BatchMeterUsageError::InvalidCustomerIdentifier(String::from(
error_message,
));
}
"InvalidProductCodeException" => {
return BatchMeterUsageError::InvalidProductCode(String::from(error_message));
}
"InvalidUsageDimensionException" => {
return BatchMeterUsageError::InvalidUsageDimension(String::from(error_message));
}
"ThrottlingException" => {
return BatchMeterUsageError::Throttling(String::from(error_message));
}
"TimestampOutOfBoundsException" => {
return BatchMeterUsageError::TimestampOutOfBounds(String::from(error_message));
}
"ValidationException" => {
return BatchMeterUsageError::Validation(error_message.to_string());
}
_ => {}
}
}
return BatchMeterUsageError::Unknown(res);
}
}
impl From<serde_json::error::Error> for BatchMeterUsageError {
fn from(err: serde_json::error::Error) -> BatchMeterUsageError {
BatchMeterUsageError::ParseError(err.description().to_string())
}
}
impl From<CredentialsError> for BatchMeterUsageError {
fn from(err: CredentialsError) -> BatchMeterUsageError {
BatchMeterUsageError::Credentials(err)
}
}
impl From<HttpDispatchError> for BatchMeterUsageError {
fn from(err: HttpDispatchError) -> BatchMeterUsageError {
BatchMeterUsageError::HttpDispatch(err)
}
}
impl From<io::Error> for BatchMeterUsageError {
fn from(err: io::Error) -> BatchMeterUsageError {
BatchMeterUsageError::HttpDispatch(HttpDispatchError::from(err))
}
}
impl fmt::Display for BatchMeterUsageError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.description())
}
}
impl Error for BatchMeterUsageError {
fn description(&self) -> &str {
match *self {
BatchMeterUsageError::DisabledApi(ref cause) => cause,
BatchMeterUsageError::InternalServiceError(ref cause) => cause,
BatchMeterUsageError::InvalidCustomerIdentifier(ref cause) => cause,
BatchMeterUsageError::InvalidProductCode(ref cause) => cause,
BatchMeterUsageError::InvalidUsageDimension(ref cause) => cause,
BatchMeterUsageError::Throttling(ref cause) => cause,
BatchMeterUsageError::TimestampOutOfBounds(ref cause) => cause,
BatchMeterUsageError::Validation(ref cause) => cause,
BatchMeterUsageError::Credentials(ref err) => err.description(),
BatchMeterUsageError::HttpDispatch(ref dispatch_error) => dispatch_error.description(),
BatchMeterUsageError::ParseError(ref cause) => cause,
BatchMeterUsageError::Unknown(_) => "unknown error",
}
}
}
#[derive(Debug, PartialEq)]
pub enum MeterUsageError {
DuplicateRequest(String),
InternalServiceError(String),
InvalidEndpointRegion(String),
InvalidProductCode(String),
InvalidUsageDimension(String),
Throttling(String),
TimestampOutOfBounds(String),
HttpDispatch(HttpDispatchError),
Credentials(CredentialsError),
Validation(String),
ParseError(String),
Unknown(BufferedHttpResponse),
}
impl MeterUsageError {
pub fn from_response(res: BufferedHttpResponse) -> MeterUsageError {
if let Ok(json) = from_slice::<SerdeJsonValue>(&res.body) {
let raw_error_type = json
.get("__type")
.and_then(|e| e.as_str())
.unwrap_or("Unknown");
let error_message = json.get("message").and_then(|m| m.as_str()).unwrap_or("");
let pieces: Vec<&str> = raw_error_type.split("#").collect();
let error_type = pieces.last().expect("Expected error type");
match *error_type {
"DuplicateRequestException" => {
return MeterUsageError::DuplicateRequest(String::from(error_message));
}
"InternalServiceErrorException" => {
return MeterUsageError::InternalServiceError(String::from(error_message));
}
"InvalidEndpointRegionException" => {
return MeterUsageError::InvalidEndpointRegion(String::from(error_message));
}
"InvalidProductCodeException" => {
return MeterUsageError::InvalidProductCode(String::from(error_message));
}
"InvalidUsageDimensionException" => {
return MeterUsageError::InvalidUsageDimension(String::from(error_message));
}
"ThrottlingException" => {
return MeterUsageError::Throttling(String::from(error_message));
}
"TimestampOutOfBoundsException" => {
return MeterUsageError::TimestampOutOfBounds(String::from(error_message));
}
"ValidationException" => {
return MeterUsageError::Validation(error_message.to_string());
}
_ => {}
}
}
return MeterUsageError::Unknown(res);
}
}
impl From<serde_json::error::Error> for MeterUsageError {
fn from(err: serde_json::error::Error) -> MeterUsageError {
MeterUsageError::ParseError(err.description().to_string())
}
}
impl From<CredentialsError> for MeterUsageError {
fn from(err: CredentialsError) -> MeterUsageError {
MeterUsageError::Credentials(err)
}
}
impl From<HttpDispatchError> for MeterUsageError {
fn from(err: HttpDispatchError) -> MeterUsageError {
MeterUsageError::HttpDispatch(err)
}
}
impl From<io::Error> for MeterUsageError {
fn from(err: io::Error) -> MeterUsageError {
MeterUsageError::HttpDispatch(HttpDispatchError::from(err))
}
}
impl fmt::Display for MeterUsageError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.description())
}
}
impl Error for MeterUsageError {
fn description(&self) -> &str {
match *self {
MeterUsageError::DuplicateRequest(ref cause) => cause,
MeterUsageError::InternalServiceError(ref cause) => cause,
MeterUsageError::InvalidEndpointRegion(ref cause) => cause,
MeterUsageError::InvalidProductCode(ref cause) => cause,
MeterUsageError::InvalidUsageDimension(ref cause) => cause,
MeterUsageError::Throttling(ref cause) => cause,
MeterUsageError::TimestampOutOfBounds(ref cause) => cause,
MeterUsageError::Validation(ref cause) => cause,
MeterUsageError::Credentials(ref err) => err.description(),
MeterUsageError::HttpDispatch(ref dispatch_error) => dispatch_error.description(),
MeterUsageError::ParseError(ref cause) => cause,
MeterUsageError::Unknown(_) => "unknown error",
}
}
}
#[derive(Debug, PartialEq)]
pub enum RegisterUsageError {
CustomerNotEntitled(String),
DisabledApi(String),
InternalServiceError(String),
InvalidProductCode(String),
InvalidPublicKeyVersion(String),
InvalidRegion(String),
PlatformNotSupported(String),
Throttling(String),
HttpDispatch(HttpDispatchError),
Credentials(CredentialsError),
Validation(String),
ParseError(String),
Unknown(BufferedHttpResponse),
}
impl RegisterUsageError {
pub fn from_response(res: BufferedHttpResponse) -> RegisterUsageError {
if let Ok(json) = from_slice::<SerdeJsonValue>(&res.body) {
let raw_error_type = json
.get("__type")
.and_then(|e| e.as_str())
.unwrap_or("Unknown");
let error_message = json.get("message").and_then(|m| m.as_str()).unwrap_or("");
let pieces: Vec<&str> = raw_error_type.split("#").collect();
let error_type = pieces.last().expect("Expected error type");
match *error_type {
"CustomerNotEntitledException" => {
return RegisterUsageError::CustomerNotEntitled(String::from(error_message));
}
"DisabledApiException" => {
return RegisterUsageError::DisabledApi(String::from(error_message));
}
"InternalServiceErrorException" => {
return RegisterUsageError::InternalServiceError(String::from(error_message));
}
"InvalidProductCodeException" => {
return RegisterUsageError::InvalidProductCode(String::from(error_message));
}
"InvalidPublicKeyVersionException" => {
return RegisterUsageError::InvalidPublicKeyVersion(String::from(error_message));
}
"InvalidRegionException" => {
return RegisterUsageError::InvalidRegion(String::from(error_message));
}
"PlatformNotSupportedException" => {
return RegisterUsageError::PlatformNotSupported(String::from(error_message));
}
"ThrottlingException" => {
return RegisterUsageError::Throttling(String::from(error_message));
}
"ValidationException" => {
return RegisterUsageError::Validation(error_message.to_string());
}
_ => {}
}
}
return RegisterUsageError::Unknown(res);
}
}
impl From<serde_json::error::Error> for RegisterUsageError {
fn from(err: serde_json::error::Error) -> RegisterUsageError {
RegisterUsageError::ParseError(err.description().to_string())
}
}
impl From<CredentialsError> for RegisterUsageError {
fn from(err: CredentialsError) -> RegisterUsageError {
RegisterUsageError::Credentials(err)
}
}
impl From<HttpDispatchError> for RegisterUsageError {
fn from(err: HttpDispatchError) -> RegisterUsageError {
RegisterUsageError::HttpDispatch(err)
}
}
impl From<io::Error> for RegisterUsageError {
fn from(err: io::Error) -> RegisterUsageError {
RegisterUsageError::HttpDispatch(HttpDispatchError::from(err))
}
}
impl fmt::Display for RegisterUsageError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.description())
}
}
impl Error for RegisterUsageError {
fn description(&self) -> &str {
match *self {
RegisterUsageError::CustomerNotEntitled(ref cause) => cause,
RegisterUsageError::DisabledApi(ref cause) => cause,
RegisterUsageError::InternalServiceError(ref cause) => cause,
RegisterUsageError::InvalidProductCode(ref cause) => cause,
RegisterUsageError::InvalidPublicKeyVersion(ref cause) => cause,
RegisterUsageError::InvalidRegion(ref cause) => cause,
RegisterUsageError::PlatformNotSupported(ref cause) => cause,
RegisterUsageError::Throttling(ref cause) => cause,
RegisterUsageError::Validation(ref cause) => cause,
RegisterUsageError::Credentials(ref err) => err.description(),
RegisterUsageError::HttpDispatch(ref dispatch_error) => dispatch_error.description(),
RegisterUsageError::ParseError(ref cause) => cause,
RegisterUsageError::Unknown(_) => "unknown error",
}
}
}
#[derive(Debug, PartialEq)]
pub enum ResolveCustomerError {
DisabledApi(String),
ExpiredToken(String),
InternalServiceError(String),
InvalidToken(String),
Throttling(String),
HttpDispatch(HttpDispatchError),
Credentials(CredentialsError),
Validation(String),
ParseError(String),
Unknown(BufferedHttpResponse),
}
impl ResolveCustomerError {
pub fn from_response(res: BufferedHttpResponse) -> ResolveCustomerError {
if let Ok(json) = from_slice::<SerdeJsonValue>(&res.body) {
let raw_error_type = json
.get("__type")
.and_then(|e| e.as_str())
.unwrap_or("Unknown");
let error_message = json.get("message").and_then(|m| m.as_str()).unwrap_or("");
let pieces: Vec<&str> = raw_error_type.split("#").collect();
let error_type = pieces.last().expect("Expected error type");
match *error_type {
"DisabledApiException" => {
return ResolveCustomerError::DisabledApi(String::from(error_message));
}
"ExpiredTokenException" => {
return ResolveCustomerError::ExpiredToken(String::from(error_message));
}
"InternalServiceErrorException" => {
return ResolveCustomerError::InternalServiceError(String::from(error_message));
}
"InvalidTokenException" => {
return ResolveCustomerError::InvalidToken(String::from(error_message));
}
"ThrottlingException" => {
return ResolveCustomerError::Throttling(String::from(error_message));
}
"ValidationException" => {
return ResolveCustomerError::Validation(error_message.to_string());
}
_ => {}
}
}
return ResolveCustomerError::Unknown(res);
}
}
impl From<serde_json::error::Error> for ResolveCustomerError {
fn from(err: serde_json::error::Error) -> ResolveCustomerError {
ResolveCustomerError::ParseError(err.description().to_string())
}
}
impl From<CredentialsError> for ResolveCustomerError {
fn from(err: CredentialsError) -> ResolveCustomerError {
ResolveCustomerError::Credentials(err)
}
}
impl From<HttpDispatchError> for ResolveCustomerError {
fn from(err: HttpDispatchError) -> ResolveCustomerError {
ResolveCustomerError::HttpDispatch(err)
}
}
impl From<io::Error> for ResolveCustomerError {
fn from(err: io::Error) -> ResolveCustomerError {
ResolveCustomerError::HttpDispatch(HttpDispatchError::from(err))
}
}
impl fmt::Display for ResolveCustomerError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.description())
}
}
impl Error for ResolveCustomerError {
fn description(&self) -> &str {
match *self {
ResolveCustomerError::DisabledApi(ref cause) => cause,
ResolveCustomerError::ExpiredToken(ref cause) => cause,
ResolveCustomerError::InternalServiceError(ref cause) => cause,
ResolveCustomerError::InvalidToken(ref cause) => cause,
ResolveCustomerError::Throttling(ref cause) => cause,
ResolveCustomerError::Validation(ref cause) => cause,
ResolveCustomerError::Credentials(ref err) => err.description(),
ResolveCustomerError::HttpDispatch(ref dispatch_error) => dispatch_error.description(),
ResolveCustomerError::ParseError(ref cause) => cause,
ResolveCustomerError::Unknown(_) => "unknown error",
}
}
}
pub trait MarketplaceMetering {
fn batch_meter_usage(
&self,
input: BatchMeterUsageRequest,
) -> RusotoFuture<BatchMeterUsageResult, BatchMeterUsageError>;
fn meter_usage(
&self,
input: MeterUsageRequest,
) -> RusotoFuture<MeterUsageResult, MeterUsageError>;
fn register_usage(
&self,
input: RegisterUsageRequest,
) -> RusotoFuture<RegisterUsageResult, RegisterUsageError>;
fn resolve_customer(
&self,
input: ResolveCustomerRequest,
) -> RusotoFuture<ResolveCustomerResult, ResolveCustomerError>;
}
#[derive(Clone)]
pub struct MarketplaceMeteringClient {
client: Client,
region: region::Region,
}
impl MarketplaceMeteringClient {
pub fn new(region: region::Region) -> MarketplaceMeteringClient {
MarketplaceMeteringClient {
client: Client::shared(),
region: region,
}
}
pub fn new_with<P, D>(
request_dispatcher: D,
credentials_provider: P,
region: region::Region,
) -> MarketplaceMeteringClient
where
P: ProvideAwsCredentials + Send + Sync + 'static,
P::Future: Send,
D: DispatchSignedRequest + Send + Sync + 'static,
D::Future: Send,
{
MarketplaceMeteringClient {
client: Client::new_with(credentials_provider, request_dispatcher),
region: region,
}
}
}
impl MarketplaceMetering for MarketplaceMeteringClient {
fn batch_meter_usage(
&self,
input: BatchMeterUsageRequest,
) -> RusotoFuture<BatchMeterUsageResult, BatchMeterUsageError> {
let mut request = SignedRequest::new("POST", "aws-marketplace", &self.region, "/");
request.set_endpoint_prefix("metering.marketplace".to_string());
request.set_content_type("application/x-amz-json-1.1".to_owned());
request.add_header("x-amz-target", "AWSMPMeteringService.BatchMeterUsage");
let encoded = serde_json::to_string(&input).unwrap();
request.set_payload(Some(encoded.into_bytes()));
self.client.sign_and_dispatch(request, |response| {
if response.status.is_success() {
Box::new(response.buffer().from_err().map(|response| {
let mut body = response.body;
if body.is_empty() || body == b"null" {
body = b"{}".to_vec();
}
serde_json::from_str::<BatchMeterUsageResult>(
String::from_utf8_lossy(body.as_ref()).as_ref(),
)
.unwrap()
}))
} else {
Box::new(
response
.buffer()
.from_err()
.and_then(|response| Err(BatchMeterUsageError::from_response(response))),
)
}
})
}
fn meter_usage(
&self,
input: MeterUsageRequest,
) -> RusotoFuture<MeterUsageResult, MeterUsageError> {
let mut request = SignedRequest::new("POST", "aws-marketplace", &self.region, "/");
request.set_endpoint_prefix("metering.marketplace".to_string());
request.set_content_type("application/x-amz-json-1.1".to_owned());
request.add_header("x-amz-target", "AWSMPMeteringService.MeterUsage");
let encoded = serde_json::to_string(&input).unwrap();
request.set_payload(Some(encoded.into_bytes()));
self.client.sign_and_dispatch(request, |response| {
if response.status.is_success() {
Box::new(response.buffer().from_err().map(|response| {
let mut body = response.body;
if body.is_empty() || body == b"null" {
body = b"{}".to_vec();
}
serde_json::from_str::<MeterUsageResult>(
String::from_utf8_lossy(body.as_ref()).as_ref(),
)
.unwrap()
}))
} else {
Box::new(
response
.buffer()
.from_err()
.and_then(|response| Err(MeterUsageError::from_response(response))),
)
}
})
}
fn register_usage(
&self,
input: RegisterUsageRequest,
) -> RusotoFuture<RegisterUsageResult, RegisterUsageError> {
let mut request = SignedRequest::new("POST", "aws-marketplace", &self.region, "/");
request.set_endpoint_prefix("metering.marketplace".to_string());
request.set_content_type("application/x-amz-json-1.1".to_owned());
request.add_header("x-amz-target", "AWSMPMeteringService.RegisterUsage");
let encoded = serde_json::to_string(&input).unwrap();
request.set_payload(Some(encoded.into_bytes()));
self.client.sign_and_dispatch(request, |response| {
if response.status.is_success() {
Box::new(response.buffer().from_err().map(|response| {
let mut body = response.body;
if body.is_empty() || body == b"null" {
body = b"{}".to_vec();
}
serde_json::from_str::<RegisterUsageResult>(
String::from_utf8_lossy(body.as_ref()).as_ref(),
)
.unwrap()
}))
} else {
Box::new(
response
.buffer()
.from_err()
.and_then(|response| Err(RegisterUsageError::from_response(response))),
)
}
})
}
fn resolve_customer(
&self,
input: ResolveCustomerRequest,
) -> RusotoFuture<ResolveCustomerResult, ResolveCustomerError> {
let mut request = SignedRequest::new("POST", "aws-marketplace", &self.region, "/");
request.set_endpoint_prefix("metering.marketplace".to_string());
request.set_content_type("application/x-amz-json-1.1".to_owned());
request.add_header("x-amz-target", "AWSMPMeteringService.ResolveCustomer");
let encoded = serde_json::to_string(&input).unwrap();
request.set_payload(Some(encoded.into_bytes()));
self.client.sign_and_dispatch(request, |response| {
if response.status.is_success() {
Box::new(response.buffer().from_err().map(|response| {
let mut body = response.body;
if body.is_empty() || body == b"null" {
body = b"{}".to_vec();
}
serde_json::from_str::<ResolveCustomerResult>(
String::from_utf8_lossy(body.as_ref()).as_ref(),
)
.unwrap()
}))
} else {
Box::new(
response
.buffer()
.from_err()
.and_then(|response| Err(ResolveCustomerError::from_response(response))),
)
}
})
}
}
#[cfg(test)]
mod protocol_tests {}