1use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
27#[serde(rename_all = "camelCase")]
28pub struct HttpRequest {
29 #[serde(skip_serializing_if = "Option::is_none")]
30 pub method: Option<String>,
31
32 #[serde(skip_serializing_if = "Option::is_none")]
33 pub path: Option<String>,
34
35 #[serde(skip_serializing_if = "Option::is_none")]
36 pub query_string_parameters: Option<HashMap<String, Vec<String>>>,
37
38 #[serde(skip_serializing_if = "Option::is_none")]
39 pub headers: Option<HashMap<String, Vec<String>>>,
40
41 #[serde(skip_serializing_if = "Option::is_none")]
42 pub body: Option<Body>,
43}
44
45impl HttpRequest {
46 pub fn new() -> Self {
48 Self::default()
49 }
50
51 pub fn method(mut self, method: impl Into<String>) -> Self {
53 self.method = Some(method.into());
54 self
55 }
56
57 pub fn path(mut self, path: impl Into<String>) -> Self {
59 self.path = Some(path.into());
60 self
61 }
62
63 pub fn query_param(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
65 let params = self.query_string_parameters.get_or_insert_with(HashMap::new);
66 params
67 .entry(key.into())
68 .or_default()
69 .push(value.into());
70 self
71 }
72
73 pub fn header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
75 let headers = self.headers.get_or_insert_with(HashMap::new);
76 headers
77 .entry(key.into())
78 .or_default()
79 .push(value.into());
80 self
81 }
82
83 pub fn body(mut self, body: impl Into<String>) -> Self {
85 self.body = Some(Body::Plain(body.into()));
86 self
87 }
88
89 pub fn json_body(mut self, json: serde_json::Value) -> Self {
91 self.body = Some(Body::Typed {
92 body_type: "JSON".to_string(),
93 json: json.to_string(),
94 });
95 self
96 }
97
98 pub fn file_body(mut self, file_path: impl Into<String>) -> Self {
103 self.body = Some(Body::File {
104 file_path: file_path.into(),
105 content_type: None,
106 template_type: None,
107 });
108 self
109 }
110
111 pub fn body_value(mut self, body: Body) -> Self {
113 self.body = Some(body);
114 self
115 }
116}
117
118#[derive(Debug, Clone, PartialEq)]
124pub enum Body {
125 Plain(String),
127 Typed { body_type: String, json: String },
129 File {
131 file_path: String,
132 content_type: Option<String>,
133 template_type: Option<String>,
134 },
135}
136
137impl Body {
138 pub fn file(file_path: impl Into<String>) -> Self {
149 Body::File {
150 file_path: file_path.into(),
151 content_type: None,
152 template_type: None,
153 }
154 }
155
156 pub fn with_content_type(mut self, content_type: impl Into<String>) -> Self {
158 if let Body::File {
159 content_type: ref mut ct,
160 ..
161 } = self
162 {
163 *ct = Some(content_type.into());
164 }
165 self
166 }
167
168 pub fn with_template_type(mut self, template_type: impl Into<String>) -> Self {
171 if let Body::File {
172 template_type: ref mut tt,
173 ..
174 } = self
175 {
176 *tt = Some(template_type.into());
177 }
178 self
179 }
180}
181
182impl Serialize for Body {
183 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
184 where
185 S: serde::Serializer,
186 {
187 match self {
188 Body::Plain(s) => serializer.serialize_str(s),
189 Body::Typed { body_type, json } => {
190 use serde::ser::SerializeMap;
191 let mut map = serializer.serialize_map(Some(2))?;
192 map.serialize_entry("type", body_type)?;
193 map.serialize_entry("json", json)?;
194 map.end()
195 }
196 Body::File {
197 file_path,
198 content_type,
199 template_type,
200 } => {
201 use serde::ser::SerializeMap;
202 let count = 2
203 + content_type.as_ref().map_or(0, |_| 1)
204 + template_type.as_ref().map_or(0, |_| 1);
205 let mut map = serializer.serialize_map(Some(count))?;
206 map.serialize_entry("type", "FILE")?;
207 map.serialize_entry("filePath", file_path)?;
208 if let Some(ct) = content_type {
209 map.serialize_entry("contentType", ct)?;
210 }
211 if let Some(tt) = template_type {
212 map.serialize_entry("templateType", tt)?;
213 }
214 map.end()
215 }
216 }
217 }
218}
219
220impl<'de> Deserialize<'de> for Body {
221 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
222 where
223 D: serde::Deserializer<'de>,
224 {
225 use serde_json::Value;
226 let v = Value::deserialize(deserializer)?;
227 match v {
228 Value::String(s) => Ok(Body::Plain(s)),
229 Value::Object(map) => {
230 let body_type = map
231 .get("type")
232 .and_then(|v| v.as_str())
233 .unwrap_or("JSON")
234 .to_string();
235 if body_type == "FILE" {
236 let file_path = map
237 .get("filePath")
238 .and_then(|v| v.as_str())
239 .unwrap_or("")
240 .to_string();
241 let content_type = map
242 .get("contentType")
243 .and_then(|v| v.as_str())
244 .map(|s| s.to_string());
245 let template_type = map
246 .get("templateType")
247 .and_then(|v| v.as_str())
248 .map(|s| s.to_string());
249 Ok(Body::File {
250 file_path,
251 content_type,
252 template_type,
253 })
254 } else {
255 let json = map
256 .get("json")
257 .and_then(|v| v.as_str())
258 .unwrap_or("")
259 .to_string();
260 Ok(Body::Typed { body_type, json })
261 }
262 }
263 _ => Ok(Body::Plain(v.to_string())),
264 }
265 }
266}
267
268#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
284#[serde(rename_all = "camelCase")]
285pub struct HttpResponse {
286 #[serde(skip_serializing_if = "Option::is_none")]
287 pub status_code: Option<u16>,
288
289 #[serde(skip_serializing_if = "Option::is_none")]
290 pub headers: Option<HashMap<String, Vec<String>>>,
291
292 #[serde(skip_serializing_if = "Option::is_none")]
293 pub body: Option<String>,
294
295 #[serde(skip_serializing_if = "Option::is_none")]
296 pub delay: Option<Delay>,
297}
298
299impl HttpResponse {
300 pub fn new() -> Self {
302 Self::default()
303 }
304
305 pub fn status_code(mut self, code: u16) -> Self {
307 self.status_code = Some(code);
308 self
309 }
310
311 pub fn header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
313 let headers = self.headers.get_or_insert_with(HashMap::new);
314 headers
315 .entry(key.into())
316 .or_default()
317 .push(value.into());
318 self
319 }
320
321 pub fn body(mut self, body: impl Into<String>) -> Self {
323 self.body = Some(body.into());
324 self
325 }
326
327 pub fn delay(mut self, delay: Delay) -> Self {
329 self.delay = Some(delay);
330 self
331 }
332}
333
334#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
350#[serde(rename_all = "camelCase")]
351pub struct HttpTemplate {
352 #[serde(skip_serializing_if = "Option::is_none")]
353 pub template_type: Option<String>,
354
355 #[serde(skip_serializing_if = "Option::is_none")]
356 pub template: Option<String>,
357
358 #[serde(skip_serializing_if = "Option::is_none")]
359 pub template_file: Option<String>,
360}
361
362impl HttpTemplate {
363 pub fn new(template_type: impl Into<String>, template: impl Into<String>) -> Self {
365 Self {
366 template_type: Some(template_type.into()),
367 template: Some(template.into()),
368 template_file: None,
369 }
370 }
371
372 pub fn from_file(template_type: impl Into<String>, file_path: impl Into<String>) -> Self {
374 Self {
375 template_type: Some(template_type.into()),
376 template: None,
377 template_file: Some(file_path.into()),
378 }
379 }
380
381 pub fn template_type(mut self, template_type: impl Into<String>) -> Self {
383 self.template_type = Some(template_type.into());
384 self
385 }
386
387 pub fn template(mut self, template: impl Into<String>) -> Self {
389 self.template = Some(template.into());
390 self
391 }
392
393 pub fn template_file(mut self, file_path: impl Into<String>) -> Self {
395 self.template_file = Some(file_path.into());
396 self
397 }
398}
399
400#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
413#[serde(rename_all = "camelCase")]
414pub struct HttpForward {
415 pub host: String,
416
417 #[serde(skip_serializing_if = "Option::is_none")]
418 pub port: Option<u16>,
419
420 #[serde(skip_serializing_if = "Option::is_none")]
421 pub scheme: Option<String>,
422}
423
424impl HttpForward {
425 pub fn new(host: impl Into<String>, port: u16) -> Self {
427 Self {
428 host: host.into(),
429 port: Some(port),
430 scheme: None,
431 }
432 }
433
434 pub fn scheme(mut self, scheme: impl Into<String>) -> Self {
436 self.scheme = Some(scheme.into());
437 self
438 }
439}
440
441#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
447#[serde(rename_all = "camelCase")]
448pub struct HttpError {
449 #[serde(skip_serializing_if = "Option::is_none")]
450 pub drop_connection: Option<bool>,
451
452 #[serde(skip_serializing_if = "Option::is_none")]
453 pub response_bytes: Option<String>,
454}
455
456impl HttpError {
457 pub fn new() -> Self {
459 Self::default()
460 }
461
462 pub fn drop_connection(mut self, drop: bool) -> Self {
464 self.drop_connection = Some(drop);
465 self
466 }
467
468 pub fn response_bytes(mut self, bytes: impl Into<String>) -> Self {
470 self.response_bytes = Some(bytes.into());
471 self
472 }
473}
474
475#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
481#[serde(rename_all = "camelCase")]
482pub struct Delay {
483 pub time_unit: String,
484 pub value: u64,
485}
486
487impl Delay {
488 pub fn milliseconds(value: u64) -> Self {
490 Self {
491 time_unit: "MILLISECONDS".to_string(),
492 value,
493 }
494 }
495
496 pub fn seconds(value: u64) -> Self {
498 Self {
499 time_unit: "SECONDS".to_string(),
500 value,
501 }
502 }
503}
504
505#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
511#[serde(rename_all = "camelCase")]
512pub struct Times {
513 #[serde(skip_serializing_if = "Option::is_none")]
514 pub remaining_times: Option<u32>,
515
516 pub unlimited: bool,
517}
518
519impl Times {
520 pub fn unlimited() -> Self {
522 Self {
523 remaining_times: None,
524 unlimited: true,
525 }
526 }
527
528 pub fn exactly(n: u32) -> Self {
530 Self {
531 remaining_times: Some(n),
532 unlimited: false,
533 }
534 }
535
536 pub fn once() -> Self {
538 Self::exactly(1)
539 }
540}
541
542#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
548#[serde(rename_all = "camelCase")]
549pub struct TimeToLive {
550 #[serde(skip_serializing_if = "Option::is_none")]
551 pub time_unit: Option<String>,
552
553 #[serde(skip_serializing_if = "Option::is_none")]
554 pub time_to_live: Option<u64>,
555
556 pub unlimited: bool,
557}
558
559impl TimeToLive {
560 pub fn unlimited() -> Self {
562 Self {
563 time_unit: None,
564 time_to_live: None,
565 unlimited: true,
566 }
567 }
568
569 pub fn seconds(seconds: u64) -> Self {
571 Self {
572 time_unit: Some("SECONDS".to_string()),
573 time_to_live: Some(seconds),
574 unlimited: false,
575 }
576 }
577
578 pub fn milliseconds(millis: u64) -> Self {
580 Self {
581 time_unit: Some("MILLISECONDS".to_string()),
582 time_to_live: Some(millis),
583 unlimited: false,
584 }
585 }
586}
587
588#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
594#[serde(rename_all = "camelCase")]
595pub struct VerificationTimes {
596 #[serde(skip_serializing_if = "Option::is_none")]
597 pub at_least: Option<u32>,
598
599 #[serde(skip_serializing_if = "Option::is_none")]
600 pub at_most: Option<u32>,
601}
602
603impl VerificationTimes {
604 pub fn at_least(n: u32) -> Self {
606 Self {
607 at_least: Some(n),
608 at_most: None,
609 }
610 }
611
612 pub fn at_most(n: u32) -> Self {
614 Self {
615 at_least: None,
616 at_most: Some(n),
617 }
618 }
619
620 pub fn exactly(n: u32) -> Self {
622 Self {
623 at_least: Some(n),
624 at_most: Some(n),
625 }
626 }
627
628 pub fn between(min: u32, max: u32) -> Self {
630 Self {
631 at_least: Some(min),
632 at_most: Some(max),
633 }
634 }
635}
636
637#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
643#[serde(rename_all = "camelCase")]
644pub struct Expectation {
645 #[serde(skip_serializing_if = "Option::is_none")]
646 pub id: Option<String>,
647
648 #[serde(skip_serializing_if = "Option::is_none")]
649 pub priority: Option<i32>,
650
651 pub http_request: HttpRequest,
652
653 #[serde(skip_serializing_if = "Option::is_none")]
654 pub http_response: Option<HttpResponse>,
655
656 #[serde(skip_serializing_if = "Option::is_none")]
657 pub http_forward: Option<HttpForward>,
658
659 #[serde(skip_serializing_if = "Option::is_none")]
660 pub http_response_template: Option<HttpTemplate>,
661
662 #[serde(skip_serializing_if = "Option::is_none")]
663 pub http_forward_template: Option<HttpTemplate>,
664
665 #[serde(skip_serializing_if = "Option::is_none")]
666 pub http_error: Option<HttpError>,
667
668 #[serde(skip_serializing_if = "Option::is_none")]
669 pub times: Option<Times>,
670
671 #[serde(skip_serializing_if = "Option::is_none")]
672 pub time_to_live: Option<TimeToLive>,
673}
674
675impl Expectation {
676 pub fn new(request: HttpRequest) -> Self {
678 Self {
679 http_request: request,
680 ..Default::default()
681 }
682 }
683
684 pub fn id(mut self, id: impl Into<String>) -> Self {
686 self.id = Some(id.into());
687 self
688 }
689
690 pub fn priority(mut self, priority: i32) -> Self {
692 self.priority = Some(priority);
693 self
694 }
695
696 pub fn respond(mut self, response: HttpResponse) -> Self {
698 self.http_response = Some(response);
699 self
700 }
701
702 pub fn forward(mut self, forward: HttpForward) -> Self {
704 self.http_forward = Some(forward);
705 self
706 }
707
708 pub fn respond_template(mut self, template: HttpTemplate) -> Self {
710 self.http_response_template = Some(template);
711 self
712 }
713
714 pub fn forward_template(mut self, template: HttpTemplate) -> Self {
716 self.http_forward_template = Some(template);
717 self
718 }
719
720 pub fn error(mut self, error: HttpError) -> Self {
722 self.http_error = Some(error);
723 self
724 }
725
726 pub fn times(mut self, times: Times) -> Self {
728 self.times = Some(times);
729 self
730 }
731
732 pub fn time_to_live(mut self, ttl: TimeToLive) -> Self {
734 self.time_to_live = Some(ttl);
735 self
736 }
737}
738
739#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
750#[serde(rename_all = "camelCase")]
751pub struct Verification {
752 #[serde(skip_serializing_if = "Option::is_none")]
753 pub http_request: Option<HttpRequest>,
754
755 #[serde(skip_serializing_if = "Option::is_none")]
756 pub http_response: Option<HttpResponse>,
757
758 #[serde(skip_serializing_if = "Option::is_none")]
759 pub times: Option<VerificationTimes>,
760
761 #[serde(skip_serializing_if = "Option::is_none")]
762 pub maximum_number_of_request_to_return_in_verification_failure: Option<u32>,
763}
764
765#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
771#[serde(rename_all = "camelCase")]
772pub struct VerificationSequence {
773 #[serde(skip_serializing_if = "Option::is_none")]
774 pub http_requests: Option<Vec<HttpRequest>>,
775
776 #[serde(skip_serializing_if = "Option::is_none")]
777 pub http_responses: Option<Vec<HttpResponse>>,
778}
779
780#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
786pub struct Ports {
787 pub ports: Vec<u16>,
788}
789
790#[derive(Debug, Clone, Copy, PartialEq, Eq)]
796pub enum RetrieveType {
797 Requests,
799 ActiveExpectations,
801 RecordedExpectations,
803 Logs,
805 RequestResponses,
807}
808
809impl RetrieveType {
810 pub fn as_str(&self) -> &'static str {
812 match self {
813 RetrieveType::Requests => "REQUESTS",
814 RetrieveType::ActiveExpectations => "ACTIVE_EXPECTATIONS",
815 RetrieveType::RecordedExpectations => "RECORDED_EXPECTATIONS",
816 RetrieveType::Logs => "LOGS",
817 RetrieveType::RequestResponses => "REQUEST_RESPONSES",
818 }
819 }
820}
821
822#[derive(Debug, Clone, Copy, PartialEq, Eq)]
824pub enum RetrieveFormat {
825 Json,
826 LogEntries,
827}
828
829impl RetrieveFormat {
830 pub fn as_str(&self) -> &'static str {
832 match self {
833 RetrieveFormat::Json => "JSON",
834 RetrieveFormat::LogEntries => "LOG_ENTRIES",
835 }
836 }
837}
838
839#[derive(Debug, Clone, Copy, PartialEq, Eq)]
841pub enum ClearType {
842 All,
843 Log,
844 Expectations,
845}
846
847impl ClearType {
848 pub fn as_str(&self) -> &'static str {
850 match self {
851 ClearType::All => "ALL",
852 ClearType::Log => "LOG",
853 ClearType::Expectations => "EXPECTATIONS",
854 }
855 }
856}