use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use bytes::Bytes;
use http::HeaderMap;
#[derive(Debug, Clone)]
pub struct MockRequest {
pub method: String,
pub uri: String,
pub headers: HeaderMap,
pub body: Bytes,
}
#[derive(Debug, Clone)]
pub struct MockResponse {
pub status: u16,
pub headers: HeaderMap,
pub body: Bytes,
}
impl MockResponse {
pub fn xml(status: u16, body: impl Into<Bytes>) -> Self {
let mut headers = HeaderMap::new();
headers.insert(http::header::CONTENT_TYPE, "text/xml".parse().unwrap());
Self {
status,
headers,
body: body.into(),
}
}
pub fn json(status: u16, body: impl Into<Bytes>) -> Self {
let mut headers = HeaderMap::new();
headers.insert(
http::header::CONTENT_TYPE,
"application/x-amz-json-1.0".parse().unwrap(),
);
Self {
status,
headers,
body: body.into(),
}
}
pub fn rest_json(status: u16, body: impl Into<Bytes>) -> Self {
let mut headers = HeaderMap::new();
headers.insert(
http::header::CONTENT_TYPE,
"application/json".parse().unwrap(),
);
Self {
status,
headers,
body: body.into(),
}
}
pub fn cbor(status: u16, body: impl Into<Bytes>) -> Self {
let mut headers = HeaderMap::new();
headers.insert(
http::header::CONTENT_TYPE,
"application/cbor".parse().unwrap(),
);
headers.insert("smithy-protocol", "rpc-v2-cbor".parse().unwrap());
Self {
status,
headers,
body: body.into(),
}
}
pub fn error(status: u16, code: &str, message: &str) -> Self {
let body = format!(
r#"<ErrorResponse>
<Error>
<Type>Sender</Type>
<Code>{code}</Code>
<Message>{message}</Message>
</Error>
<RequestId>00000000-0000-0000-0000-000000000000</RequestId>
</ErrorResponse>"#
);
Self::xml(status, body)
}
}
pub struct StubService {
name: String,
patterns: Vec<String>,
}
impl StubService {
pub fn new(name: impl Into<String>, patterns: Vec<String>) -> Self {
Self {
name: name.into(),
patterns,
}
}
}
impl MockService for StubService {
fn service_name(&self) -> &str {
&self.name
}
fn url_patterns(&self) -> Vec<&str> {
self.patterns.iter().map(|s| s.as_str()).collect()
}
fn handle(
&self,
_request: MockRequest,
) -> Pin<Box<dyn Future<Output = MockResponse> + Send + '_>> {
let name = self.name.clone();
Box::pin(async move {
MockResponse::json(
501,
format!(
r#"{{"__type":"NotImplementedException","message":"Service '{}' is recognized but not yet implemented in winterbaume"}}"#,
name
),
)
})
}
}
pub trait MockService: Send + Sync + 'static {
fn service_name(&self) -> &str;
fn url_patterns(&self) -> Vec<&str>;
fn handle(
&self,
request: MockRequest,
) -> Pin<Box<dyn Future<Output = MockResponse> + Send + '_>>;
}
impl<T: MockService> MockService for Arc<T> {
fn service_name(&self) -> &str {
(**self).service_name()
}
fn url_patterns(&self) -> Vec<&str> {
(**self).url_patterns()
}
fn handle(
&self,
request: MockRequest,
) -> Pin<Box<dyn Future<Output = MockResponse> + Send + '_>> {
(**self).handle(request)
}
}