winterbaume_core/
service.rs1use std::future::Future;
4use std::pin::Pin;
5use std::sync::Arc;
6
7use bytes::Bytes;
8use http::HeaderMap;
9
10#[derive(Debug, Clone)]
12pub struct MockRequest {
13 pub method: String,
14 pub uri: String,
15 pub headers: HeaderMap,
16 pub body: Bytes,
17}
18
19#[derive(Debug, Clone)]
21pub struct MockResponse {
22 pub status: u16,
23 pub headers: HeaderMap,
24 pub body: Bytes,
25}
26
27impl MockResponse {
28 pub fn xml(status: u16, body: impl Into<Bytes>) -> Self {
30 let mut headers = HeaderMap::new();
31 headers.insert(http::header::CONTENT_TYPE, "text/xml".parse().unwrap());
32 Self {
33 status,
34 headers,
35 body: body.into(),
36 }
37 }
38
39 pub fn json(status: u16, body: impl Into<Bytes>) -> Self {
41 let mut headers = HeaderMap::new();
42 headers.insert(
43 http::header::CONTENT_TYPE,
44 "application/x-amz-json-1.0".parse().unwrap(),
45 );
46 Self {
47 status,
48 headers,
49 body: body.into(),
50 }
51 }
52
53 pub fn rest_json(status: u16, body: impl Into<Bytes>) -> Self {
56 let mut headers = HeaderMap::new();
57 headers.insert(
58 http::header::CONTENT_TYPE,
59 "application/json".parse().unwrap(),
60 );
61 Self {
62 status,
63 headers,
64 body: body.into(),
65 }
66 }
67
68 pub fn cbor(status: u16, body: impl Into<Bytes>) -> Self {
71 let mut headers = HeaderMap::new();
72 headers.insert(
73 http::header::CONTENT_TYPE,
74 "application/cbor".parse().unwrap(),
75 );
76 headers.insert("smithy-protocol", "rpc-v2-cbor".parse().unwrap());
77 Self {
78 status,
79 headers,
80 body: body.into(),
81 }
82 }
83
84 pub fn error(status: u16, code: &str, message: &str) -> Self {
86 let body = format!(
87 r#"<ErrorResponse>
88 <Error>
89 <Type>Sender</Type>
90 <Code>{code}</Code>
91 <Message>{message}</Message>
92 </Error>
93 <RequestId>00000000-0000-0000-0000-000000000000</RequestId>
94</ErrorResponse>"#
95 );
96 Self::xml(status, body)
97 }
98}
99
100pub struct StubService {
106 name: String,
107 patterns: Vec<String>,
108}
109
110impl StubService {
111 pub fn new(name: impl Into<String>, patterns: Vec<String>) -> Self {
113 Self {
114 name: name.into(),
115 patterns,
116 }
117 }
118}
119
120impl MockService for StubService {
121 fn service_name(&self) -> &str {
122 &self.name
123 }
124
125 fn url_patterns(&self) -> Vec<&str> {
126 self.patterns.iter().map(|s| s.as_str()).collect()
127 }
128
129 fn handle(
130 &self,
131 _request: MockRequest,
132 ) -> Pin<Box<dyn Future<Output = MockResponse> + Send + '_>> {
133 let name = self.name.clone();
134 Box::pin(async move {
135 MockResponse::json(
136 501,
137 format!(
138 r#"{{"__type":"NotImplementedException","message":"Service '{}' is recognized but not yet implemented in winterbaume"}}"#,
139 name
140 ),
141 )
142 })
143 }
144}
145
146pub trait MockService: Send + Sync + 'static {
150 fn service_name(&self) -> &str;
152
153 fn url_patterns(&self) -> Vec<&str>;
155
156 fn handle(
158 &self,
159 request: MockRequest,
160 ) -> Pin<Box<dyn Future<Output = MockResponse> + Send + '_>>;
161}
162
163impl<T: MockService> MockService for Arc<T> {
167 fn service_name(&self) -> &str {
168 (**self).service_name()
169 }
170
171 fn url_patterns(&self) -> Vec<&str> {
172 (**self).url_patterns()
173 }
174
175 fn handle(
176 &self,
177 request: MockRequest,
178 ) -> Pin<Box<dyn Future<Output = MockResponse> + Send + '_>> {
179 (**self).handle(request)
180 }
181}