1use base64::{engine::general_purpose::STANDARD as BASE64, Engine};
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9
10#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
28#[serde(rename_all = "camelCase")]
29pub struct HttpRequest {
30 #[serde(skip_serializing_if = "Option::is_none")]
31 pub method: Option<String>,
32
33 #[serde(skip_serializing_if = "Option::is_none")]
34 pub path: Option<String>,
35
36 #[serde(skip_serializing_if = "Option::is_none")]
37 pub query_string_parameters: Option<HashMap<String, Vec<String>>>,
38
39 #[serde(skip_serializing_if = "Option::is_none")]
40 pub headers: Option<HashMap<String, Vec<String>>>,
41
42 #[serde(skip_serializing_if = "Option::is_none")]
43 pub body: Option<Body>,
44
45 #[serde(skip_serializing_if = "Option::is_none")]
46 pub socket_address: Option<SocketAddress>,
47}
48
49impl HttpRequest {
50 pub fn new() -> Self {
52 Self::default()
53 }
54
55 pub fn socket_address(mut self, socket_address: SocketAddress) -> Self {
61 self.socket_address = Some(socket_address);
62 self
63 }
64
65 pub fn method(mut self, method: impl Into<String>) -> Self {
67 self.method = Some(method.into());
68 self
69 }
70
71 pub fn path(mut self, path: impl Into<String>) -> Self {
73 self.path = Some(path.into());
74 self
75 }
76
77 pub fn query_param(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
79 let params = self
80 .query_string_parameters
81 .get_or_insert_with(HashMap::new);
82 params.entry(key.into()).or_default().push(value.into());
83 self
84 }
85
86 pub fn header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
88 let headers = self.headers.get_or_insert_with(HashMap::new);
89 headers.entry(key.into()).or_default().push(value.into());
90 self
91 }
92
93 pub fn body(mut self, body: impl Into<String>) -> Self {
95 self.body = Some(Body::Plain(body.into()));
96 self
97 }
98
99 pub fn json_body(mut self, json: serde_json::Value) -> Self {
101 self.body = Some(Body::Typed {
102 body_type: "JSON".to_string(),
103 json: json.to_string(),
104 });
105 self
106 }
107
108 pub fn file_body(mut self, file_path: impl Into<String>) -> Self {
113 self.body = Some(Body::File {
114 file_path: file_path.into(),
115 content_type: None,
116 template_type: None,
117 });
118 self
119 }
120
121 pub fn body_value(mut self, body: Body) -> Self {
123 self.body = Some(body);
124 self
125 }
126}
127
128#[derive(Debug, Clone, PartialEq)]
134pub enum Body {
135 Plain(String),
137 Typed { body_type: String, json: String },
139 File {
141 file_path: String,
142 content_type: Option<String>,
143 template_type: Option<String>,
144 },
145}
146
147impl Body {
148 pub fn file(file_path: impl Into<String>) -> Self {
159 Body::File {
160 file_path: file_path.into(),
161 content_type: None,
162 template_type: None,
163 }
164 }
165
166 pub fn with_content_type(mut self, content_type: impl Into<String>) -> Self {
168 if let Body::File {
169 content_type: ref mut ct,
170 ..
171 } = self
172 {
173 *ct = Some(content_type.into());
174 }
175 self
176 }
177
178 pub fn with_template_type(mut self, template_type: impl Into<String>) -> Self {
181 if let Body::File {
182 template_type: ref mut tt,
183 ..
184 } = self
185 {
186 *tt = Some(template_type.into());
187 }
188 self
189 }
190}
191
192impl Serialize for Body {
193 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
194 where
195 S: serde::Serializer,
196 {
197 match self {
198 Body::Plain(s) => serializer.serialize_str(s),
199 Body::Typed { body_type, json } => {
200 use serde::ser::SerializeMap;
201 let mut map = serializer.serialize_map(Some(2))?;
202 map.serialize_entry("type", body_type)?;
203 map.serialize_entry("json", json)?;
204 map.end()
205 }
206 Body::File {
207 file_path,
208 content_type,
209 template_type,
210 } => {
211 use serde::ser::SerializeMap;
212 let count = 2
213 + content_type.as_ref().map_or(0, |_| 1)
214 + template_type.as_ref().map_or(0, |_| 1);
215 let mut map = serializer.serialize_map(Some(count))?;
216 map.serialize_entry("type", "FILE")?;
217 map.serialize_entry("filePath", file_path)?;
218 if let Some(ct) = content_type {
219 map.serialize_entry("contentType", ct)?;
220 }
221 if let Some(tt) = template_type {
222 map.serialize_entry("templateType", tt)?;
223 }
224 map.end()
225 }
226 }
227 }
228}
229
230impl<'de> Deserialize<'de> for Body {
231 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
232 where
233 D: serde::Deserializer<'de>,
234 {
235 use serde_json::Value;
236 let v = Value::deserialize(deserializer)?;
237 match v {
238 Value::String(s) => Ok(Body::Plain(s)),
239 Value::Object(map) => {
240 let body_type = map
241 .get("type")
242 .and_then(|v| v.as_str())
243 .unwrap_or("JSON")
244 .to_string();
245 if body_type == "FILE" {
246 let file_path = map
247 .get("filePath")
248 .and_then(|v| v.as_str())
249 .unwrap_or("")
250 .to_string();
251 let content_type = map
252 .get("contentType")
253 .and_then(|v| v.as_str())
254 .map(|s| s.to_string());
255 let template_type = map
256 .get("templateType")
257 .and_then(|v| v.as_str())
258 .map(|s| s.to_string());
259 Ok(Body::File {
260 file_path,
261 content_type,
262 template_type,
263 })
264 } else {
265 let json = map
266 .get("json")
267 .and_then(|v| v.as_str())
268 .unwrap_or("")
269 .to_string();
270 Ok(Body::Typed { body_type, json })
271 }
272 }
273 _ => Ok(Body::Plain(v.to_string())),
274 }
275 }
276}
277
278#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
294#[serde(rename_all = "camelCase")]
295pub struct HttpResponse {
296 #[serde(skip_serializing_if = "Option::is_none")]
297 pub status_code: Option<u16>,
298
299 #[serde(skip_serializing_if = "Option::is_none")]
300 pub headers: Option<HashMap<String, Vec<String>>>,
301
302 #[serde(skip_serializing_if = "Option::is_none")]
303 pub body: Option<String>,
304
305 #[serde(skip_serializing_if = "Option::is_none")]
306 pub delay: Option<Delay>,
307}
308
309impl HttpResponse {
310 pub fn new() -> Self {
312 Self::default()
313 }
314
315 pub fn status_code(mut self, code: u16) -> Self {
317 self.status_code = Some(code);
318 self
319 }
320
321 pub fn header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
323 let headers = self.headers.get_or_insert_with(HashMap::new);
324 headers.entry(key.into()).or_default().push(value.into());
325 self
326 }
327
328 pub fn body(mut self, body: impl Into<String>) -> Self {
330 self.body = Some(body.into());
331 self
332 }
333
334 pub fn delay(mut self, delay: Delay) -> Self {
336 self.delay = Some(delay);
337 self
338 }
339}
340
341#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
357#[serde(rename_all = "camelCase")]
358pub struct HttpTemplate {
359 #[serde(skip_serializing_if = "Option::is_none")]
360 pub template_type: Option<String>,
361
362 #[serde(skip_serializing_if = "Option::is_none")]
363 pub template: Option<String>,
364
365 #[serde(skip_serializing_if = "Option::is_none")]
366 pub template_file: Option<String>,
367}
368
369impl HttpTemplate {
370 pub fn new(template_type: impl Into<String>, template: impl Into<String>) -> Self {
372 Self {
373 template_type: Some(template_type.into()),
374 template: Some(template.into()),
375 template_file: None,
376 }
377 }
378
379 pub fn from_file(template_type: impl Into<String>, file_path: impl Into<String>) -> Self {
381 Self {
382 template_type: Some(template_type.into()),
383 template: None,
384 template_file: Some(file_path.into()),
385 }
386 }
387
388 pub fn template_type(mut self, template_type: impl Into<String>) -> Self {
390 self.template_type = Some(template_type.into());
391 self
392 }
393
394 pub fn template(mut self, template: impl Into<String>) -> Self {
396 self.template = Some(template.into());
397 self
398 }
399
400 pub fn template_file(mut self, file_path: impl Into<String>) -> Self {
402 self.template_file = Some(file_path.into());
403 self
404 }
405}
406
407#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
420#[serde(rename_all = "camelCase")]
421pub struct HttpForward {
422 pub host: String,
423
424 #[serde(skip_serializing_if = "Option::is_none")]
425 pub port: Option<u16>,
426
427 #[serde(skip_serializing_if = "Option::is_none")]
428 pub scheme: Option<String>,
429}
430
431impl HttpForward {
432 pub fn new(host: impl Into<String>, port: u16) -> Self {
434 Self {
435 host: host.into(),
436 port: Some(port),
437 scheme: None,
438 }
439 }
440
441 pub fn scheme(mut self, scheme: impl Into<String>) -> Self {
443 self.scheme = Some(scheme.into());
444 self
445 }
446}
447
448#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
466#[serde(rename_all = "camelCase")]
467pub struct HttpClassCallback {
468 pub callback_class: String,
469
470 #[serde(skip_serializing_if = "Option::is_none")]
471 pub delay: Option<Delay>,
472
473 #[serde(skip_serializing_if = "Option::is_none")]
474 pub primary: Option<bool>,
475}
476
477impl HttpClassCallback {
478 pub fn new(callback_class: impl Into<String>) -> Self {
481 Self {
482 callback_class: callback_class.into(),
483 delay: None,
484 primary: None,
485 }
486 }
487
488 pub fn delay(mut self, delay: Delay) -> Self {
490 self.delay = Some(delay);
491 self
492 }
493
494 pub fn primary(mut self, primary: bool) -> Self {
496 self.primary = Some(primary);
497 self
498 }
499}
500
501#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
519#[serde(rename_all = "camelCase")]
520pub struct HttpObjectCallback {
521 pub client_id: String,
522
523 #[serde(skip_serializing_if = "Option::is_none")]
524 pub response_callback: Option<bool>,
525
526 #[serde(skip_serializing_if = "Option::is_none")]
527 pub delay: Option<Delay>,
528
529 #[serde(skip_serializing_if = "Option::is_none")]
530 pub primary: Option<bool>,
531}
532
533impl HttpObjectCallback {
534 pub fn new(client_id: impl Into<String>) -> Self {
536 Self {
537 client_id: client_id.into(),
538 response_callback: None,
539 delay: None,
540 primary: None,
541 }
542 }
543
544 pub fn response_callback(mut self, response_callback: bool) -> Self {
546 self.response_callback = Some(response_callback);
547 self
548 }
549
550 pub fn delay(mut self, delay: Delay) -> Self {
552 self.delay = Some(delay);
553 self
554 }
555
556 pub fn primary(mut self, primary: bool) -> Self {
558 self.primary = Some(primary);
559 self
560 }
561}
562
563#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
569#[serde(rename_all = "camelCase")]
570pub struct HttpError {
571 #[serde(skip_serializing_if = "Option::is_none")]
572 pub drop_connection: Option<bool>,
573
574 #[serde(skip_serializing_if = "Option::is_none")]
575 pub response_bytes: Option<String>,
576}
577
578impl HttpError {
579 pub fn new() -> Self {
581 Self::default()
582 }
583
584 pub fn drop_connection(mut self, drop: bool) -> Self {
586 self.drop_connection = Some(drop);
587 self
588 }
589
590 pub fn response_bytes(mut self, bytes: impl Into<String>) -> Self {
592 self.response_bytes = Some(bytes.into());
593 self
594 }
595}
596
597#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
605#[serde(rename_all = "camelCase")]
606pub struct SseEvent {
607 #[serde(skip_serializing_if = "Option::is_none")]
608 pub event: Option<String>,
609
610 #[serde(skip_serializing_if = "Option::is_none")]
611 pub data: Option<String>,
612
613 #[serde(skip_serializing_if = "Option::is_none")]
614 pub id: Option<String>,
615
616 #[serde(skip_serializing_if = "Option::is_none")]
617 pub retry: Option<u32>,
618
619 #[serde(skip_serializing_if = "Option::is_none")]
620 pub delay: Option<Delay>,
621}
622
623impl SseEvent {
624 pub fn new() -> Self {
626 Self::default()
627 }
628
629 pub fn event(mut self, event: impl Into<String>) -> Self {
631 self.event = Some(event.into());
632 self
633 }
634
635 pub fn data(mut self, data: impl Into<String>) -> Self {
637 self.data = Some(data.into());
638 self
639 }
640
641 pub fn id(mut self, id: impl Into<String>) -> Self {
643 self.id = Some(id.into());
644 self
645 }
646
647 pub fn retry(mut self, retry: u32) -> Self {
649 self.retry = Some(retry);
650 self
651 }
652
653 pub fn delay(mut self, delay: Delay) -> Self {
655 self.delay = Some(delay);
656 self
657 }
658}
659
660#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
675#[serde(rename_all = "camelCase")]
676pub struct HttpSseResponse {
677 #[serde(skip_serializing_if = "Option::is_none")]
678 pub status_code: Option<u16>,
679
680 #[serde(skip_serializing_if = "Option::is_none")]
681 pub headers: Option<HashMap<String, Vec<String>>>,
682
683 #[serde(skip_serializing_if = "Option::is_none")]
684 pub events: Option<Vec<SseEvent>>,
685
686 #[serde(skip_serializing_if = "Option::is_none")]
687 pub close_connection: Option<bool>,
688
689 #[serde(skip_serializing_if = "Option::is_none")]
690 pub delay: Option<Delay>,
691}
692
693impl HttpSseResponse {
694 pub fn new() -> Self {
696 Self::default()
697 }
698
699 pub fn status_code(mut self, code: u16) -> Self {
701 self.status_code = Some(code);
702 self
703 }
704
705 pub fn header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
707 let headers = self.headers.get_or_insert_with(HashMap::new);
708 headers.entry(key.into()).or_default().push(value.into());
709 self
710 }
711
712 pub fn event(mut self, event: SseEvent) -> Self {
714 self.events.get_or_insert_with(Vec::new).push(event);
715 self
716 }
717
718 pub fn events(mut self, events: Vec<SseEvent>) -> Self {
720 self.events = Some(events);
721 self
722 }
723
724 pub fn close_connection(mut self, close: bool) -> Self {
726 self.close_connection = Some(close);
727 self
728 }
729
730 pub fn delay(mut self, delay: Delay) -> Self {
732 self.delay = Some(delay);
733 self
734 }
735}
736
737#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
746#[serde(rename_all = "camelCase")]
747pub struct WebSocketMessage {
748 #[serde(skip_serializing_if = "Option::is_none")]
749 pub text: Option<String>,
750
751 #[serde(skip_serializing_if = "Option::is_none")]
752 pub binary: Option<String>,
753
754 #[serde(skip_serializing_if = "Option::is_none")]
755 pub delay: Option<Delay>,
756}
757
758impl WebSocketMessage {
759 pub fn text(text: impl Into<String>) -> Self {
761 Self {
762 text: Some(text.into()),
763 binary: None,
764 delay: None,
765 }
766 }
767
768 pub fn binary(data: impl AsRef<[u8]>) -> Self {
770 Self {
771 text: None,
772 binary: Some(BASE64.encode(data.as_ref())),
773 delay: None,
774 }
775 }
776
777 pub fn binary_base64(base64: impl Into<String>) -> Self {
779 Self {
780 text: None,
781 binary: Some(base64.into()),
782 delay: None,
783 }
784 }
785
786 pub fn delay(mut self, delay: Delay) -> Self {
788 self.delay = Some(delay);
789 self
790 }
791}
792
793#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
808#[serde(rename_all = "camelCase")]
809pub struct HttpWebSocketResponse {
810 #[serde(skip_serializing_if = "Option::is_none")]
811 pub subprotocol: Option<String>,
812
813 #[serde(skip_serializing_if = "Option::is_none")]
814 pub messages: Option<Vec<WebSocketMessage>>,
815
816 #[serde(skip_serializing_if = "Option::is_none")]
817 pub close_connection: Option<bool>,
818
819 #[serde(skip_serializing_if = "Option::is_none")]
820 pub delay: Option<Delay>,
821}
822
823impl HttpWebSocketResponse {
824 pub fn new() -> Self {
826 Self::default()
827 }
828
829 pub fn subprotocol(mut self, subprotocol: impl Into<String>) -> Self {
831 self.subprotocol = Some(subprotocol.into());
832 self
833 }
834
835 pub fn message(mut self, message: WebSocketMessage) -> Self {
837 self.messages.get_or_insert_with(Vec::new).push(message);
838 self
839 }
840
841 pub fn messages(mut self, messages: Vec<WebSocketMessage>) -> Self {
843 self.messages = Some(messages);
844 self
845 }
846
847 pub fn close_connection(mut self, close: bool) -> Self {
849 self.close_connection = Some(close);
850 self
851 }
852
853 pub fn delay(mut self, delay: Delay) -> Self {
855 self.delay = Some(delay);
856 self
857 }
858}
859
860#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
866#[serde(rename_all = "camelCase")]
867pub struct DnsRecord {
868 #[serde(skip_serializing_if = "Option::is_none")]
869 pub name: Option<String>,
870
871 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
872 pub record_type: Option<String>,
873
874 #[serde(skip_serializing_if = "Option::is_none")]
875 pub dns_class: Option<String>,
876
877 #[serde(skip_serializing_if = "Option::is_none")]
878 pub ttl: Option<u32>,
879
880 #[serde(skip_serializing_if = "Option::is_none")]
881 pub value: Option<String>,
882
883 #[serde(skip_serializing_if = "Option::is_none")]
884 pub priority: Option<u32>,
885
886 #[serde(skip_serializing_if = "Option::is_none")]
887 pub weight: Option<u32>,
888
889 #[serde(skip_serializing_if = "Option::is_none")]
890 pub port: Option<u16>,
891}
892
893impl DnsRecord {
894 pub fn new() -> Self {
896 Self::default()
897 }
898
899 pub fn a(name: impl Into<String>, ip: impl Into<String>) -> Self {
901 Self::new().name(name).record_type("A").value(ip)
902 }
903
904 pub fn aaaa(name: impl Into<String>, ip: impl Into<String>) -> Self {
906 Self::new().name(name).record_type("AAAA").value(ip)
907 }
908
909 pub fn cname(name: impl Into<String>, target: impl Into<String>) -> Self {
911 Self::new().name(name).record_type("CNAME").value(target)
912 }
913
914 pub fn txt(name: impl Into<String>, text: impl Into<String>) -> Self {
916 Self::new().name(name).record_type("TXT").value(text)
917 }
918
919 pub fn name(mut self, name: impl Into<String>) -> Self {
921 self.name = Some(name.into());
922 self
923 }
924
925 pub fn record_type(mut self, record_type: impl Into<String>) -> Self {
927 self.record_type = Some(record_type.into());
928 self
929 }
930
931 pub fn dns_class(mut self, dns_class: impl Into<String>) -> Self {
933 self.dns_class = Some(dns_class.into());
934 self
935 }
936
937 pub fn ttl(mut self, ttl: u32) -> Self {
939 self.ttl = Some(ttl);
940 self
941 }
942
943 pub fn value(mut self, value: impl Into<String>) -> Self {
945 self.value = Some(value.into());
946 self
947 }
948
949 pub fn priority(mut self, priority: u32) -> Self {
951 self.priority = Some(priority);
952 self
953 }
954
955 pub fn weight(mut self, weight: u32) -> Self {
957 self.weight = Some(weight);
958 self
959 }
960
961 pub fn port(mut self, port: u16) -> Self {
963 self.port = Some(port);
964 self
965 }
966}
967
968#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
981#[serde(rename_all = "camelCase")]
982pub struct DnsResponse {
983 #[serde(skip_serializing_if = "Option::is_none")]
984 pub answer_records: Option<Vec<DnsRecord>>,
985
986 #[serde(skip_serializing_if = "Option::is_none")]
987 pub authority_records: Option<Vec<DnsRecord>>,
988
989 #[serde(skip_serializing_if = "Option::is_none")]
990 pub additional_records: Option<Vec<DnsRecord>>,
991
992 #[serde(skip_serializing_if = "Option::is_none")]
993 pub response_code: Option<String>,
994
995 #[serde(skip_serializing_if = "Option::is_none")]
996 pub delay: Option<Delay>,
997}
998
999impl DnsResponse {
1000 pub fn new() -> Self {
1002 Self::default()
1003 }
1004
1005 pub fn answer_record(mut self, record: DnsRecord) -> Self {
1007 self.answer_records
1008 .get_or_insert_with(Vec::new)
1009 .push(record);
1010 self
1011 }
1012
1013 pub fn answer_records(mut self, records: Vec<DnsRecord>) -> Self {
1015 self.answer_records = Some(records);
1016 self
1017 }
1018
1019 pub fn authority_record(mut self, record: DnsRecord) -> Self {
1021 self.authority_records
1022 .get_or_insert_with(Vec::new)
1023 .push(record);
1024 self
1025 }
1026
1027 pub fn additional_record(mut self, record: DnsRecord) -> Self {
1029 self.additional_records
1030 .get_or_insert_with(Vec::new)
1031 .push(record);
1032 self
1033 }
1034
1035 pub fn response_code(mut self, code: impl Into<String>) -> Self {
1037 self.response_code = Some(code.into());
1038 self
1039 }
1040
1041 pub fn delay(mut self, delay: Delay) -> Self {
1043 self.delay = Some(delay);
1044 self
1045 }
1046}
1047
1048#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
1065#[serde(rename_all = "camelCase")]
1066pub struct BinaryResponse {
1067 #[serde(skip_serializing_if = "Option::is_none")]
1068 pub binary_data: Option<String>,
1069
1070 #[serde(skip_serializing_if = "Option::is_none")]
1071 pub delay: Option<Delay>,
1072}
1073
1074impl BinaryResponse {
1075 pub fn new() -> Self {
1077 Self::default()
1078 }
1079
1080 pub fn from_bytes(data: impl AsRef<[u8]>) -> Self {
1082 Self {
1083 binary_data: Some(BASE64.encode(data.as_ref())),
1084 delay: None,
1085 }
1086 }
1087
1088 pub fn from_base64(base64: impl Into<String>) -> Self {
1090 Self {
1091 binary_data: Some(base64.into()),
1092 delay: None,
1093 }
1094 }
1095
1096 pub fn binary_data(mut self, data: impl AsRef<[u8]>) -> Self {
1098 self.binary_data = Some(BASE64.encode(data.as_ref()));
1099 self
1100 }
1101
1102 pub fn delay(mut self, delay: Delay) -> Self {
1104 self.delay = Some(delay);
1105 self
1106 }
1107}
1108
1109#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
1115#[serde(rename_all = "camelCase")]
1116pub struct GrpcStreamMessage {
1117 #[serde(skip_serializing_if = "Option::is_none")]
1118 pub json: Option<String>,
1119
1120 #[serde(skip_serializing_if = "Option::is_none")]
1121 pub delay: Option<Delay>,
1122}
1123
1124impl GrpcStreamMessage {
1125 pub fn json(json: impl Into<String>) -> Self {
1127 Self {
1128 json: Some(json.into()),
1129 delay: None,
1130 }
1131 }
1132
1133 pub fn delay(mut self, delay: Delay) -> Self {
1135 self.delay = Some(delay);
1136 self
1137 }
1138}
1139
1140#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
1154#[serde(rename_all = "camelCase")]
1155pub struct GrpcStreamResponse {
1156 #[serde(skip_serializing_if = "Option::is_none")]
1157 pub status_name: Option<String>,
1158
1159 #[serde(skip_serializing_if = "Option::is_none")]
1160 pub status_message: Option<String>,
1161
1162 #[serde(skip_serializing_if = "Option::is_none")]
1163 pub headers: Option<HashMap<String, Vec<String>>>,
1164
1165 #[serde(skip_serializing_if = "Option::is_none")]
1166 pub messages: Option<Vec<GrpcStreamMessage>>,
1167
1168 #[serde(skip_serializing_if = "Option::is_none")]
1169 pub close_connection: Option<bool>,
1170
1171 #[serde(skip_serializing_if = "Option::is_none")]
1172 pub delay: Option<Delay>,
1173}
1174
1175impl GrpcStreamResponse {
1176 pub fn new() -> Self {
1178 Self::default()
1179 }
1180
1181 pub fn status_name(mut self, status_name: impl Into<String>) -> Self {
1183 self.status_name = Some(status_name.into());
1184 self
1185 }
1186
1187 pub fn status_message(mut self, status_message: impl Into<String>) -> Self {
1189 self.status_message = Some(status_message.into());
1190 self
1191 }
1192
1193 pub fn header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
1195 let headers = self.headers.get_or_insert_with(HashMap::new);
1196 headers.entry(key.into()).or_default().push(value.into());
1197 self
1198 }
1199
1200 pub fn message(mut self, message: GrpcStreamMessage) -> Self {
1202 self.messages.get_or_insert_with(Vec::new).push(message);
1203 self
1204 }
1205
1206 pub fn messages(mut self, messages: Vec<GrpcStreamMessage>) -> Self {
1208 self.messages = Some(messages);
1209 self
1210 }
1211
1212 pub fn close_connection(mut self, close: bool) -> Self {
1214 self.close_connection = Some(close);
1215 self
1216 }
1217
1218 pub fn delay(mut self, delay: Delay) -> Self {
1220 self.delay = Some(delay);
1221 self
1222 }
1223}
1224
1225#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
1246#[serde(rename_all = "camelCase")]
1247pub struct OpenApiExpectation {
1248 pub spec_url_or_payload: String,
1249
1250 #[serde(skip_serializing_if = "Option::is_none")]
1251 pub operations_and_responses: Option<HashMap<String, String>>,
1252
1253 #[serde(skip_serializing_if = "Option::is_none")]
1254 pub context_path_prefix: Option<String>,
1255}
1256
1257impl OpenApiExpectation {
1258 pub fn new(spec_url_or_payload: impl Into<String>) -> Self {
1261 Self {
1262 spec_url_or_payload: spec_url_or_payload.into(),
1263 operations_and_responses: None,
1264 context_path_prefix: None,
1265 }
1266 }
1267
1268 pub fn operation(
1273 mut self,
1274 operation_id: impl Into<String>,
1275 status_code: impl Into<String>,
1276 ) -> Self {
1277 self.operations_and_responses
1278 .get_or_insert_with(HashMap::new)
1279 .insert(operation_id.into(), status_code.into());
1280 self
1281 }
1282
1283 pub fn operations_and_responses(mut self, map: HashMap<String, String>) -> Self {
1285 self.operations_and_responses = Some(map);
1286 self
1287 }
1288
1289 pub fn context_path_prefix(mut self, prefix: impl Into<String>) -> Self {
1291 self.context_path_prefix = Some(prefix.into());
1292 self
1293 }
1294}
1295
1296#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1302#[serde(rename_all = "camelCase")]
1303pub struct Delay {
1304 pub time_unit: String,
1305 pub value: u64,
1306}
1307
1308impl Delay {
1309 pub fn milliseconds(value: u64) -> Self {
1311 Self {
1312 time_unit: "MILLISECONDS".to_string(),
1313 value,
1314 }
1315 }
1316
1317 pub fn seconds(value: u64) -> Self {
1319 Self {
1320 time_unit: "SECONDS".to_string(),
1321 value,
1322 }
1323 }
1324}
1325
1326#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1332#[serde(rename_all = "camelCase")]
1333pub struct Times {
1334 #[serde(skip_serializing_if = "Option::is_none")]
1335 pub remaining_times: Option<u32>,
1336
1337 #[serde(default)]
1338 pub unlimited: bool,
1339}
1340
1341impl Times {
1342 pub fn unlimited() -> Self {
1344 Self {
1345 remaining_times: None,
1346 unlimited: true,
1347 }
1348 }
1349
1350 pub fn exactly(n: u32) -> Self {
1352 Self {
1353 remaining_times: Some(n),
1354 unlimited: false,
1355 }
1356 }
1357
1358 pub fn once() -> Self {
1360 Self::exactly(1)
1361 }
1362}
1363
1364#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1370#[serde(rename_all = "camelCase")]
1371pub struct TimeToLive {
1372 #[serde(skip_serializing_if = "Option::is_none")]
1373 pub time_unit: Option<String>,
1374
1375 #[serde(skip_serializing_if = "Option::is_none")]
1376 pub time_to_live: Option<u64>,
1377
1378 #[serde(default)]
1379 pub unlimited: bool,
1380}
1381
1382impl TimeToLive {
1383 pub fn unlimited() -> Self {
1385 Self {
1386 time_unit: None,
1387 time_to_live: None,
1388 unlimited: true,
1389 }
1390 }
1391
1392 pub fn seconds(seconds: u64) -> Self {
1394 Self {
1395 time_unit: Some("SECONDS".to_string()),
1396 time_to_live: Some(seconds),
1397 unlimited: false,
1398 }
1399 }
1400
1401 pub fn milliseconds(millis: u64) -> Self {
1403 Self {
1404 time_unit: Some("MILLISECONDS".to_string()),
1405 time_to_live: Some(millis),
1406 unlimited: false,
1407 }
1408 }
1409}
1410
1411#[derive(Debug, Clone, Deserialize, PartialEq)]
1423#[serde(rename_all = "camelCase")]
1424pub struct VerificationTimes {
1425 pub at_least: Option<u32>,
1426 pub at_most: Option<u32>,
1427}
1428
1429impl Serialize for VerificationTimes {
1430 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1431 where
1432 S: serde::Serializer,
1433 {
1434 use serde::ser::SerializeStruct;
1435 let mut state = serializer.serialize_struct("VerificationTimes", 2)?;
1436 state.serialize_field("atLeast", &self.at_least.map_or(-1_i64, i64::from))?;
1437 state.serialize_field("atMost", &self.at_most.map_or(-1_i64, i64::from))?;
1438 state.end()
1439 }
1440}
1441
1442impl VerificationTimes {
1443 pub fn at_least(n: u32) -> Self {
1445 Self {
1446 at_least: Some(n),
1447 at_most: None,
1448 }
1449 }
1450
1451 pub fn at_most(n: u32) -> Self {
1453 Self {
1454 at_least: None,
1455 at_most: Some(n),
1456 }
1457 }
1458
1459 pub fn exactly(n: u32) -> Self {
1461 Self {
1462 at_least: Some(n),
1463 at_most: Some(n),
1464 }
1465 }
1466
1467 pub fn between(min: u32, max: u32) -> Self {
1469 Self {
1470 at_least: Some(min),
1471 at_most: Some(max),
1472 }
1473 }
1474}
1475
1476#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
1490#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
1491pub enum ResponseMode {
1492 Sequential,
1494 Random,
1496 Weighted,
1498 Switch,
1500}
1501
1502#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
1505#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
1506pub enum CrossProtocolTrigger {
1507 DnsQuery,
1509 WebsocketConnect,
1511 GrpcRequest,
1513 HttpRequest,
1515}
1516
1517#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1535#[serde(rename_all = "camelCase")]
1536pub struct CrossProtocolScenario {
1537 pub trigger: CrossProtocolTrigger,
1538
1539 #[serde(skip_serializing_if = "Option::is_none")]
1540 pub match_pattern: Option<String>,
1541
1542 pub scenario_name: String,
1543
1544 pub target_state: String,
1545}
1546
1547impl CrossProtocolScenario {
1548 pub fn new(
1551 trigger: CrossProtocolTrigger,
1552 scenario_name: impl Into<String>,
1553 target_state: impl Into<String>,
1554 ) -> Self {
1555 Self {
1556 trigger,
1557 match_pattern: None,
1558 scenario_name: scenario_name.into(),
1559 target_state: target_state.into(),
1560 }
1561 }
1562
1563 pub fn match_pattern(mut self, pattern: impl Into<String>) -> Self {
1565 self.match_pattern = Some(pattern.into());
1566 self
1567 }
1568}
1569
1570#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
1576#[serde(rename_all = "camelCase")]
1577pub struct Expectation {
1578 #[serde(skip_serializing_if = "Option::is_none")]
1579 pub id: Option<String>,
1580
1581 #[serde(skip_serializing_if = "Option::is_none")]
1582 pub priority: Option<i32>,
1583
1584 pub http_request: HttpRequest,
1585
1586 #[serde(skip_serializing_if = "Option::is_none")]
1587 pub http_response: Option<HttpResponse>,
1588
1589 #[serde(skip_serializing_if = "Option::is_none")]
1590 pub http_forward: Option<HttpForward>,
1591
1592 #[serde(skip_serializing_if = "Option::is_none")]
1593 pub http_response_template: Option<HttpTemplate>,
1594
1595 #[serde(skip_serializing_if = "Option::is_none")]
1596 pub http_forward_template: Option<HttpTemplate>,
1597
1598 #[serde(skip_serializing_if = "Option::is_none")]
1599 pub http_error: Option<HttpError>,
1600
1601 #[serde(skip_serializing_if = "Option::is_none")]
1603 pub http_response_class_callback: Option<HttpClassCallback>,
1604
1605 #[serde(skip_serializing_if = "Option::is_none")]
1607 pub http_forward_class_callback: Option<HttpClassCallback>,
1608
1609 #[serde(skip_serializing_if = "Option::is_none")]
1611 pub http_response_object_callback: Option<HttpObjectCallback>,
1612
1613 #[serde(skip_serializing_if = "Option::is_none")]
1615 pub http_forward_object_callback: Option<HttpObjectCallback>,
1616
1617 #[serde(skip_serializing_if = "Option::is_none")]
1618 pub http_sse_response: Option<HttpSseResponse>,
1619
1620 #[serde(skip_serializing_if = "Option::is_none")]
1621 pub http_web_socket_response: Option<HttpWebSocketResponse>,
1622
1623 #[serde(skip_serializing_if = "Option::is_none")]
1624 pub dns_response: Option<DnsResponse>,
1625
1626 #[serde(skip_serializing_if = "Option::is_none")]
1627 pub binary_response: Option<BinaryResponse>,
1628
1629 #[serde(skip_serializing_if = "Option::is_none")]
1630 pub grpc_stream_response: Option<GrpcStreamResponse>,
1631
1632 #[serde(skip_serializing_if = "Option::is_none")]
1633 pub times: Option<Times>,
1634
1635 #[serde(skip_serializing_if = "Option::is_none")]
1636 pub time_to_live: Option<TimeToLive>,
1637
1638 #[serde(skip_serializing_if = "Option::is_none")]
1640 pub scenario_name: Option<String>,
1641
1642 #[serde(skip_serializing_if = "Option::is_none")]
1644 pub scenario_state: Option<String>,
1645
1646 #[serde(skip_serializing_if = "Option::is_none")]
1648 pub new_scenario_state: Option<String>,
1649
1650 #[serde(skip_serializing_if = "Option::is_none")]
1652 pub http_responses: Option<Vec<HttpResponse>>,
1653
1654 #[serde(skip_serializing_if = "Option::is_none")]
1656 pub response_mode: Option<ResponseMode>,
1657
1658 #[serde(skip_serializing_if = "Option::is_none")]
1660 pub response_weights: Option<Vec<i32>>,
1661
1662 #[serde(skip_serializing_if = "Option::is_none")]
1664 pub switch_after: Option<i32>,
1665
1666 #[serde(skip_serializing_if = "Option::is_none")]
1668 pub cross_protocol_scenarios: Option<Vec<CrossProtocolScenario>>,
1669}
1670
1671impl Expectation {
1672 pub fn new(request: HttpRequest) -> Self {
1674 Self {
1675 http_request: request,
1676 ..Default::default()
1677 }
1678 }
1679
1680 pub fn id(mut self, id: impl Into<String>) -> Self {
1682 self.id = Some(id.into());
1683 self
1684 }
1685
1686 pub fn priority(mut self, priority: i32) -> Self {
1688 self.priority = Some(priority);
1689 self
1690 }
1691
1692 pub fn respond(mut self, response: HttpResponse) -> Self {
1694 self.http_response = Some(response);
1695 self
1696 }
1697
1698 pub fn forward(mut self, forward: HttpForward) -> Self {
1700 self.http_forward = Some(forward);
1701 self
1702 }
1703
1704 pub fn respond_template(mut self, template: HttpTemplate) -> Self {
1706 self.http_response_template = Some(template);
1707 self
1708 }
1709
1710 pub fn forward_template(mut self, template: HttpTemplate) -> Self {
1712 self.http_forward_template = Some(template);
1713 self
1714 }
1715
1716 pub fn error(mut self, error: HttpError) -> Self {
1718 self.http_error = Some(error);
1719 self
1720 }
1721
1722 pub fn respond_with_class_callback(mut self, callback_class: impl Into<String>) -> Self {
1729 self.http_response_class_callback = Some(HttpClassCallback::new(callback_class));
1730 self
1731 }
1732
1733 pub fn respond_class_callback(mut self, callback: HttpClassCallback) -> Self {
1735 self.http_response_class_callback = Some(callback);
1736 self
1737 }
1738
1739 pub fn forward_with_class_callback(mut self, callback_class: impl Into<String>) -> Self {
1741 self.http_forward_class_callback = Some(HttpClassCallback::new(callback_class));
1742 self
1743 }
1744
1745 pub fn forward_class_callback(mut self, callback: HttpClassCallback) -> Self {
1747 self.http_forward_class_callback = Some(callback);
1748 self
1749 }
1750
1751 pub fn respond_object_callback(mut self, callback: HttpObjectCallback) -> Self {
1758 self.http_response_object_callback = Some(callback);
1759 self
1760 }
1761
1762 pub fn forward_object_callback(mut self, callback: HttpObjectCallback) -> Self {
1764 self.http_forward_object_callback = Some(callback);
1765 self
1766 }
1767
1768 pub fn respond_sse(mut self, sse: HttpSseResponse) -> Self {
1770 self.http_sse_response = Some(sse);
1771 self
1772 }
1773
1774 pub fn respond_web_socket(mut self, ws: HttpWebSocketResponse) -> Self {
1776 self.http_web_socket_response = Some(ws);
1777 self
1778 }
1779
1780 pub fn respond_dns(mut self, dns: DnsResponse) -> Self {
1782 self.dns_response = Some(dns);
1783 self
1784 }
1785
1786 pub fn respond_binary(mut self, binary: BinaryResponse) -> Self {
1788 self.binary_response = Some(binary);
1789 self
1790 }
1791
1792 pub fn respond_grpc_stream(mut self, grpc: GrpcStreamResponse) -> Self {
1794 self.grpc_stream_response = Some(grpc);
1795 self
1796 }
1797
1798 pub fn times(mut self, times: Times) -> Self {
1800 self.times = Some(times);
1801 self
1802 }
1803
1804 pub fn time_to_live(mut self, ttl: TimeToLive) -> Self {
1806 self.time_to_live = Some(ttl);
1807 self
1808 }
1809
1810 pub fn scenario_name(mut self, name: impl Into<String>) -> Self {
1812 self.scenario_name = Some(name.into());
1813 self
1814 }
1815
1816 pub fn scenario_state(mut self, state: impl Into<String>) -> Self {
1818 self.scenario_state = Some(state.into());
1819 self
1820 }
1821
1822 pub fn new_scenario_state(mut self, state: impl Into<String>) -> Self {
1824 self.new_scenario_state = Some(state.into());
1825 self
1826 }
1827
1828 pub fn respond_with(mut self, response: HttpResponse) -> Self {
1833 self.http_responses
1834 .get_or_insert_with(Vec::new)
1835 .push(response);
1836 self
1837 }
1838
1839 pub fn http_responses(mut self, responses: Vec<HttpResponse>) -> Self {
1841 self.http_responses = Some(responses);
1842 self
1843 }
1844
1845 pub fn response_mode(mut self, mode: ResponseMode) -> Self {
1847 self.response_mode = Some(mode);
1848 self
1849 }
1850
1851 pub fn response_weights(mut self, weights: Vec<i32>) -> Self {
1853 self.response_weights = Some(weights);
1854 self
1855 }
1856
1857 pub fn switch_after(mut self, switch_after: i32) -> Self {
1860 self.switch_after = Some(switch_after);
1861 self
1862 }
1863
1864 pub fn cross_protocol_scenario(mut self, scenario: CrossProtocolScenario) -> Self {
1866 self.cross_protocol_scenarios
1867 .get_or_insert_with(Vec::new)
1868 .push(scenario);
1869 self
1870 }
1871
1872 pub fn cross_protocol_scenarios(mut self, scenarios: Vec<CrossProtocolScenario>) -> Self {
1874 self.cross_protocol_scenarios = Some(scenarios);
1875 self
1876 }
1877}
1878
1879#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1890#[serde(rename_all = "camelCase")]
1891pub struct Verification {
1892 #[serde(skip_serializing_if = "Option::is_none")]
1893 pub http_request: Option<HttpRequest>,
1894
1895 #[serde(skip_serializing_if = "Option::is_none")]
1896 pub http_response: Option<HttpResponse>,
1897
1898 #[serde(skip_serializing_if = "Option::is_none")]
1899 pub times: Option<VerificationTimes>,
1900
1901 #[serde(skip_serializing_if = "Option::is_none")]
1902 pub maximum_number_of_request_to_return_in_verification_failure: Option<u32>,
1903}
1904
1905#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1911#[serde(rename_all = "camelCase")]
1912pub struct VerificationSequence {
1913 #[serde(skip_serializing_if = "Option::is_none")]
1914 pub http_requests: Option<Vec<HttpRequest>>,
1915
1916 #[serde(skip_serializing_if = "Option::is_none")]
1917 pub http_responses: Option<Vec<HttpResponse>>,
1918}
1919
1920#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1926pub struct Ports {
1927 pub ports: Vec<u16>,
1928}
1929
1930#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1937#[serde(rename_all = "camelCase")]
1938pub struct ScenarioState {
1939 pub scenario_name: String,
1941 pub current_state: String,
1943}
1944
1945#[derive(Debug, Clone, Deserialize)]
1948pub(crate) struct ScenarioList {
1949 #[serde(default)]
1950 pub scenarios: Vec<ScenarioState>,
1951}
1952
1953#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1959pub enum RetrieveType {
1960 Requests,
1962 ActiveExpectations,
1964 RecordedExpectations,
1966 Logs,
1968 RequestResponses,
1970}
1971
1972impl RetrieveType {
1973 pub fn as_str(&self) -> &'static str {
1975 match self {
1976 RetrieveType::Requests => "REQUESTS",
1977 RetrieveType::ActiveExpectations => "ACTIVE_EXPECTATIONS",
1978 RetrieveType::RecordedExpectations => "RECORDED_EXPECTATIONS",
1979 RetrieveType::Logs => "LOGS",
1980 RetrieveType::RequestResponses => "REQUEST_RESPONSES",
1981 }
1982 }
1983}
1984
1985#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1991pub enum RetrieveFormat {
1992 Json,
1993 LogEntries,
1994 Java,
1995 JavaScript,
1996 Python,
1997 Go,
1998 CSharp,
1999 Ruby,
2000 Rust,
2001 Php,
2002}
2003
2004impl RetrieveFormat {
2005 pub fn as_str(&self) -> &'static str {
2007 match self {
2008 RetrieveFormat::Json => "JSON",
2009 RetrieveFormat::LogEntries => "LOG_ENTRIES",
2010 RetrieveFormat::Java => "JAVA",
2011 RetrieveFormat::JavaScript => "JAVASCRIPT",
2012 RetrieveFormat::Python => "PYTHON",
2013 RetrieveFormat::Go => "GO",
2014 RetrieveFormat::CSharp => "CSHARP",
2015 RetrieveFormat::Ruby => "RUBY",
2016 RetrieveFormat::Rust => "RUST",
2017 RetrieveFormat::Php => "PHP",
2018 }
2019 }
2020}
2021
2022#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2024pub enum ClearType {
2025 All,
2026 Log,
2027 Expectations,
2028}
2029
2030impl ClearType {
2031 pub fn as_str(&self) -> &'static str {
2033 match self {
2034 ClearType::All => "ALL",
2035 ClearType::Log => "LOG",
2036 ClearType::Expectations => "EXPECTATIONS",
2037 }
2038 }
2039}
2040
2041#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
2053#[serde(rename_all = "camelCase")]
2054pub struct GrpcMethod {
2055 pub name: String,
2057
2058 pub input_type: String,
2060
2061 pub output_type: String,
2063
2064 pub client_streaming: bool,
2066
2067 pub server_streaming: bool,
2069}
2070
2071#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
2078#[serde(rename_all = "camelCase")]
2079pub struct GrpcService {
2080 pub name: String,
2082
2083 pub methods: Vec<GrpcMethod>,
2085}
2086
2087#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
2096#[serde(rename_all = "camelCase")]
2097pub struct SocketAddress {
2098 pub host: String,
2100
2101 pub port: u16,
2103
2104 #[serde(skip_serializing_if = "Option::is_none")]
2107 pub scheme: Option<String>,
2108}
2109
2110impl SocketAddress {
2111 pub fn new(host: impl Into<String>, port: u16) -> Self {
2113 Self {
2114 host: host.into(),
2115 port,
2116 scheme: None,
2117 }
2118 }
2119
2120 pub fn scheme(mut self, scheme: impl Into<String>) -> Self {
2122 self.scheme = Some(scheme.into());
2123 self
2124 }
2125
2126 pub fn https(host: impl Into<String>, port: u16) -> Self {
2128 Self::new(host, port).scheme("HTTPS")
2129 }
2130}
2131
2132#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
2141#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
2142pub enum RampCurve {
2143 Linear,
2145 Quadratic,
2147 Exponential,
2149}
2150
2151#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
2157#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
2158pub enum LoadStageType {
2159 Vu,
2161 Rate,
2163 Pause,
2165}
2166
2167#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2176#[serde(rename_all = "camelCase")]
2177pub struct LoadStage {
2178 #[serde(rename = "type")]
2180 pub stage_type: LoadStageType,
2181
2182 pub duration_millis: u64,
2184
2185 #[serde(skip_serializing_if = "Option::is_none")]
2187 pub curve: Option<RampCurve>,
2188
2189 #[serde(skip_serializing_if = "Option::is_none")]
2191 pub vus: Option<u32>,
2192
2193 #[serde(skip_serializing_if = "Option::is_none")]
2195 pub start_vus: Option<u32>,
2196
2197 #[serde(skip_serializing_if = "Option::is_none")]
2199 pub end_vus: Option<u32>,
2200
2201 #[serde(skip_serializing_if = "Option::is_none")]
2203 pub rate: Option<f64>,
2204
2205 #[serde(skip_serializing_if = "Option::is_none")]
2207 pub start_rate: Option<f64>,
2208
2209 #[serde(skip_serializing_if = "Option::is_none")]
2211 pub end_rate: Option<f64>,
2212
2213 #[serde(skip_serializing_if = "Option::is_none")]
2215 pub max_vus: Option<u32>,
2216}
2217
2218impl LoadStage {
2219 fn base(stage_type: LoadStageType, duration_millis: u64) -> Self {
2220 Self {
2221 stage_type,
2222 duration_millis,
2223 curve: None,
2224 vus: None,
2225 start_vus: None,
2226 end_vus: None,
2227 rate: None,
2228 start_rate: None,
2229 end_rate: None,
2230 max_vus: None,
2231 }
2232 }
2233
2234 pub fn vu_hold(vus: u32, duration_millis: u64) -> Self {
2236 let mut stage = Self::base(LoadStageType::Vu, duration_millis);
2237 stage.vus = Some(vus);
2238 stage
2239 }
2240
2241 pub fn vu_ramp(start_vus: u32, end_vus: u32, duration_millis: u64, curve: RampCurve) -> Self {
2244 let mut stage = Self::base(LoadStageType::Vu, duration_millis);
2245 stage.start_vus = Some(start_vus);
2246 stage.end_vus = Some(end_vus);
2247 stage.curve = Some(curve);
2248 stage
2249 }
2250
2251 pub fn rate_hold(rate: f64, duration_millis: u64) -> Self {
2253 let mut stage = Self::base(LoadStageType::Rate, duration_millis);
2254 stage.rate = Some(rate);
2255 stage
2256 }
2257
2258 pub fn rate_ramp(
2261 start_rate: f64,
2262 end_rate: f64,
2263 duration_millis: u64,
2264 curve: RampCurve,
2265 ) -> Self {
2266 let mut stage = Self::base(LoadStageType::Rate, duration_millis);
2267 stage.start_rate = Some(start_rate);
2268 stage.end_rate = Some(end_rate);
2269 stage.curve = Some(curve);
2270 stage
2271 }
2272
2273 pub fn pause(duration_millis: u64) -> Self {
2275 Self::base(LoadStageType::Pause, duration_millis)
2276 }
2277
2278 pub fn max_vus(mut self, max_vus: u32) -> Self {
2280 self.max_vus = Some(max_vus);
2281 self
2282 }
2283}
2284
2285#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
2292#[serde(rename_all = "camelCase")]
2293pub struct LoadProfile {
2294 pub stages: Vec<LoadStage>,
2296}
2297
2298impl LoadProfile {
2299 pub fn of(stages: Vec<LoadStage>) -> Self {
2301 Self { stages }
2302 }
2303
2304 pub fn constant(vus: u32, duration_millis: u64) -> Self {
2306 Self::of(vec![LoadStage::vu_hold(vus, duration_millis)])
2307 }
2308
2309 pub fn linear(start_vus: u32, end_vus: u32, duration_millis: u64) -> Self {
2312 Self::of(vec![LoadStage::vu_ramp(
2313 start_vus,
2314 end_vus,
2315 duration_millis,
2316 RampCurve::Linear,
2317 )])
2318 }
2319
2320 pub fn add_stage(mut self, stage: LoadStage) -> Self {
2322 self.stages.push(stage);
2323 self
2324 }
2325}
2326
2327#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2330#[serde(rename_all = "camelCase")]
2331pub struct LoadStep {
2332 pub request: HttpRequest,
2334
2335 #[serde(skip_serializing_if = "Option::is_none")]
2337 pub think_time: Option<Delay>,
2338}
2339
2340impl LoadStep {
2341 pub fn new(request: HttpRequest) -> Self {
2343 Self {
2344 request,
2345 think_time: None,
2346 }
2347 }
2348
2349 pub fn think_time(mut self, delay: Delay) -> Self {
2351 self.think_time = Some(delay);
2352 self
2353 }
2354}
2355
2356#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2363#[serde(rename_all = "camelCase")]
2364pub struct LoadScenario {
2365 pub name: String,
2367
2368 #[serde(skip_serializing_if = "Option::is_none")]
2371 pub template_type: Option<String>,
2372
2373 #[serde(skip_serializing_if = "Option::is_none")]
2375 pub max_requests: Option<u64>,
2376
2377 #[serde(skip_serializing_if = "Option::is_none")]
2381 pub start_delay_millis: Option<u64>,
2382
2383 pub profile: LoadProfile,
2385
2386 pub steps: Vec<LoadStep>,
2388}
2389
2390impl LoadScenario {
2391 pub fn new(name: impl Into<String>, profile: LoadProfile, steps: Vec<LoadStep>) -> Self {
2393 Self {
2394 name: name.into(),
2395 template_type: None,
2396 max_requests: None,
2397 start_delay_millis: None,
2398 profile,
2399 steps,
2400 }
2401 }
2402
2403 pub fn template_type(mut self, template_type: impl Into<String>) -> Self {
2405 self.template_type = Some(template_type.into());
2406 self
2407 }
2408
2409 pub fn max_requests(mut self, max_requests: u64) -> Self {
2411 self.max_requests = Some(max_requests);
2412 self
2413 }
2414
2415 pub fn start_delay_millis(mut self, start_delay_millis: u64) -> Self {
2418 self.start_delay_millis = Some(start_delay_millis);
2419 self
2420 }
2421}
2422
2423#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2430#[serde(rename_all = "camelCase")]
2431pub struct SloObjective {
2432 pub sli: String,
2435
2436 pub comparator: String,
2440
2441 pub threshold: f64,
2444
2445 #[serde(skip_serializing_if = "Option::is_none")]
2448 pub scope: Option<String>,
2449}
2450
2451impl SloObjective {
2452 pub fn new(sli: impl Into<String>, comparator: impl Into<String>, threshold: f64) -> Self {
2454 Self {
2455 sli: sli.into(),
2456 comparator: comparator.into(),
2457 threshold,
2458 scope: None,
2459 }
2460 }
2461
2462 pub fn scope(mut self, scope: impl Into<String>) -> Self {
2464 self.scope = Some(scope.into());
2465 self
2466 }
2467}
2468
2469#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
2472#[serde(rename_all = "camelCase")]
2473pub struct SloWindow {
2474 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
2476 pub window_type: Option<String>,
2477
2478 #[serde(skip_serializing_if = "Option::is_none")]
2480 pub lookback_millis: Option<u64>,
2481
2482 #[serde(skip_serializing_if = "Option::is_none")]
2484 pub from_epoch_millis: Option<u64>,
2485
2486 #[serde(skip_serializing_if = "Option::is_none")]
2488 pub to_epoch_millis: Option<u64>,
2489}
2490
2491impl SloWindow {
2492 pub fn lookback(millis: u64) -> Self {
2494 Self {
2495 window_type: Some("LOOKBACK".to_string()),
2496 lookback_millis: Some(millis),
2497 ..Default::default()
2498 }
2499 }
2500
2501 pub fn explicit(from_epoch_millis: u64, to_epoch_millis: u64) -> Self {
2503 Self {
2504 window_type: Some("EXPLICIT".to_string()),
2505 from_epoch_millis: Some(from_epoch_millis),
2506 to_epoch_millis: Some(to_epoch_millis),
2507 ..Default::default()
2508 }
2509 }
2510}
2511
2512#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2515#[serde(rename_all = "camelCase")]
2516pub struct SloCriteria {
2517 #[serde(skip_serializing_if = "Option::is_none")]
2519 pub name: Option<String>,
2520
2521 #[serde(skip_serializing_if = "Option::is_none")]
2523 pub window: Option<SloWindow>,
2524
2525 #[serde(skip_serializing_if = "Option::is_none")]
2528 pub minimum_sample_count: Option<u64>,
2529
2530 #[serde(skip_serializing_if = "Option::is_none")]
2532 pub upstream_hosts: Option<Vec<String>>,
2533
2534 pub objectives: Vec<SloObjective>,
2536}
2537
2538impl SloCriteria {
2539 pub fn new(objectives: Vec<SloObjective>) -> Self {
2541 Self {
2542 name: None,
2543 window: None,
2544 minimum_sample_count: None,
2545 upstream_hosts: None,
2546 objectives,
2547 }
2548 }
2549
2550 pub fn name(mut self, name: impl Into<String>) -> Self {
2552 self.name = Some(name.into());
2553 self
2554 }
2555
2556 pub fn window(mut self, window: SloWindow) -> Self {
2558 self.window = Some(window);
2559 self
2560 }
2561
2562 pub fn minimum_sample_count(mut self, count: u64) -> Self {
2564 self.minimum_sample_count = Some(count);
2565 self
2566 }
2567
2568 pub fn upstream_hosts(mut self, hosts: Vec<String>) -> Self {
2570 self.upstream_hosts = Some(hosts);
2571 self
2572 }
2573}
2574
2575#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2578#[serde(rename_all = "camelCase")]
2579pub struct SloObjectiveResult {
2580 #[serde(skip_serializing_if = "Option::is_none")]
2581 pub sli: Option<String>,
2582 #[serde(skip_serializing_if = "Option::is_none")]
2583 pub comparator: Option<String>,
2584 #[serde(skip_serializing_if = "Option::is_none")]
2585 pub threshold: Option<f64>,
2586 #[serde(skip_serializing_if = "Option::is_none")]
2587 pub observed_value: Option<f64>,
2588 #[serde(skip_serializing_if = "Option::is_none")]
2590 pub result: Option<String>,
2591 #[serde(skip_serializing_if = "Option::is_none")]
2592 pub detail: Option<String>,
2593}
2594
2595#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
2598#[serde(rename_all = "camelCase")]
2599pub struct SloVerdict {
2600 #[serde(skip_serializing_if = "Option::is_none")]
2601 pub name: Option<String>,
2602 #[serde(skip_serializing_if = "Option::is_none")]
2604 pub result: Option<String>,
2605 #[serde(skip_serializing_if = "Option::is_none")]
2606 pub window_from_epoch_millis: Option<u64>,
2607 #[serde(skip_serializing_if = "Option::is_none")]
2608 pub window_to_epoch_millis: Option<u64>,
2609 #[serde(skip_serializing_if = "Option::is_none")]
2610 pub sample_count: Option<u64>,
2611 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2612 pub objective_results: Vec<SloObjectiveResult>,
2613}
2614
2615impl SloVerdict {
2616 pub fn is_pass(&self) -> bool {
2618 self.result.as_deref() == Some("PASS")
2619 }
2620
2621 pub fn is_fail(&self) -> bool {
2623 self.result.as_deref() == Some("FAIL")
2624 }
2625
2626 pub fn is_inconclusive(&self) -> bool {
2628 self.result.as_deref() == Some("INCONCLUSIVE")
2629 }
2630}
2631
2632#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
2639#[serde(rename_all = "camelCase")]
2640pub struct PreemptionRequest {
2641 #[serde(skip_serializing_if = "Option::is_none")]
2644 pub mode: Option<String>,
2645
2646 #[serde(skip_serializing_if = "Option::is_none")]
2648 pub drain_millis: Option<u64>,
2649
2650 #[serde(skip_serializing_if = "Option::is_none")]
2653 pub ttl_millis: Option<u64>,
2654
2655 #[serde(skip_serializing_if = "Option::is_none")]
2658 pub last_stream_id: Option<i64>,
2659}
2660
2661impl PreemptionRequest {
2662 pub fn new() -> Self {
2664 Self::default()
2665 }
2666
2667 pub fn mode(mut self, mode: impl Into<String>) -> Self {
2669 self.mode = Some(mode.into());
2670 self
2671 }
2672
2673 pub fn drain_millis(mut self, millis: u64) -> Self {
2675 self.drain_millis = Some(millis);
2676 self
2677 }
2678
2679 pub fn ttl_millis(mut self, millis: u64) -> Self {
2681 self.ttl_millis = Some(millis);
2682 self
2683 }
2684
2685 pub fn last_stream_id(mut self, id: i64) -> Self {
2687 self.last_stream_id = Some(id);
2688 self
2689 }
2690}
2691
2692#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
2695#[serde(rename_all = "camelCase")]
2696pub struct PreemptionStatus {
2697 #[serde(skip_serializing_if = "Option::is_none")]
2699 pub state: Option<String>,
2700
2701 #[serde(skip_serializing_if = "Option::is_none")]
2703 pub in_flight: Option<u64>,
2704
2705 #[serde(skip_serializing_if = "Option::is_none")]
2707 pub drain_remaining_millis: Option<u64>,
2708
2709 #[serde(skip_serializing_if = "Option::is_none")]
2711 pub mode: Option<String>,
2712}
2713
2714#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
2722#[serde(rename_all = "camelCase")]
2723pub struct HttpChaosProfile {
2724 #[serde(skip_serializing_if = "Option::is_none")]
2726 pub error_status: Option<u16>,
2727
2728 #[serde(skip_serializing_if = "Option::is_none")]
2730 pub error_probability: Option<f64>,
2731
2732 #[serde(skip_serializing_if = "Option::is_none")]
2734 pub latency: Option<Delay>,
2735
2736 #[serde(skip_serializing_if = "Option::is_none")]
2738 pub connection_drop: Option<bool>,
2739
2740 #[serde(skip_serializing_if = "Option::is_none")]
2742 pub seed: Option<i64>,
2743
2744 #[serde(flatten)]
2746 pub extra: HashMap<String, serde_json::Value>,
2747}
2748
2749impl HttpChaosProfile {
2750 pub fn new() -> Self {
2752 Self::default()
2753 }
2754
2755 pub fn error_status(mut self, status: u16) -> Self {
2757 self.error_status = Some(status);
2758 self
2759 }
2760
2761 pub fn error_probability(mut self, probability: f64) -> Self {
2763 self.error_probability = Some(probability);
2764 self
2765 }
2766
2767 pub fn latency(mut self, latency: Delay) -> Self {
2769 self.latency = Some(latency);
2770 self
2771 }
2772
2773 pub fn connection_drop(mut self, drop: bool) -> Self {
2775 self.connection_drop = Some(drop);
2776 self
2777 }
2778
2779 pub fn seed(mut self, seed: i64) -> Self {
2781 self.seed = Some(seed);
2782 self
2783 }
2784}
2785
2786#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
2793#[serde(rename_all = "camelCase")]
2794pub struct ChaosStage {
2795 pub duration_millis: u64,
2797
2798 pub profiles: HashMap<String, HttpChaosProfile>,
2800}
2801
2802impl ChaosStage {
2803 pub fn new(duration_millis: u64) -> Self {
2805 Self {
2806 duration_millis,
2807 profiles: HashMap::new(),
2808 }
2809 }
2810
2811 pub fn profile(mut self, host: impl Into<String>, profile: HttpChaosProfile) -> Self {
2813 self.profiles.insert(host.into(), profile);
2814 self
2815 }
2816}
2817
2818#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2821#[serde(rename_all = "camelCase")]
2822pub struct ChaosExperiment {
2823 #[serde(skip_serializing_if = "Option::is_none")]
2825 pub name: Option<String>,
2826
2827 #[serde(rename = "loop", skip_serializing_if = "Option::is_none")]
2830 pub loop_back: Option<bool>,
2831
2832 pub stages: Vec<ChaosStage>,
2834}
2835
2836impl ChaosExperiment {
2837 pub fn new(stages: Vec<ChaosStage>) -> Self {
2839 Self {
2840 name: None,
2841 loop_back: None,
2842 stages,
2843 }
2844 }
2845
2846 pub fn name(mut self, name: impl Into<String>) -> Self {
2848 self.name = Some(name.into());
2849 self
2850 }
2851
2852 pub fn loop_back(mut self, loop_back: bool) -> Self {
2854 self.loop_back = Some(loop_back);
2855 self
2856 }
2857}
2858
2859#[cfg(test)]
2864mod tests {
2865 use super::*;
2866
2867 #[test]
2868 fn test_grpc_services_deserialize_from_server_wire_shape() {
2869 let wire = r#"[
2872 {
2873 "name": "helloworld.Greeter",
2874 "methods": [
2875 {
2876 "name": "SayHello",
2877 "inputType": "helloworld.HelloRequest",
2878 "outputType": "helloworld.HelloReply",
2879 "clientStreaming": false,
2880 "serverStreaming": false
2881 },
2882 {
2883 "name": "LotsOfReplies",
2884 "inputType": "helloworld.HelloRequest",
2885 "outputType": "helloworld.HelloReply",
2886 "clientStreaming": false,
2887 "serverStreaming": true
2888 }
2889 ]
2890 }
2891 ]"#;
2892
2893 let services: Vec<GrpcService> = serde_json::from_str(wire).unwrap();
2894 assert_eq!(services.len(), 1);
2895 let svc = &services[0];
2896 assert_eq!(svc.name, "helloworld.Greeter");
2897 assert_eq!(svc.methods.len(), 2);
2898
2899 let unary = &svc.methods[0];
2900 assert_eq!(unary.name, "SayHello");
2901 assert_eq!(unary.input_type, "helloworld.HelloRequest");
2902 assert_eq!(unary.output_type, "helloworld.HelloReply");
2903 assert!(!unary.client_streaming);
2904 assert!(!unary.server_streaming);
2905
2906 let server_stream = &svc.methods[1];
2907 assert_eq!(server_stream.name, "LotsOfReplies");
2908 assert!(!server_stream.client_streaming);
2909 assert!(server_stream.server_streaming);
2910 }
2911
2912 #[test]
2913 fn test_grpc_method_serializes_with_camel_case_keys() {
2914 let method = GrpcMethod {
2915 name: "BidiChat".into(),
2916 input_type: "chat.Message".into(),
2917 output_type: "chat.Message".into(),
2918 client_streaming: true,
2919 server_streaming: true,
2920 };
2921 let value = serde_json::to_value(&method).unwrap();
2922 assert_eq!(value["name"], "BidiChat");
2923 assert_eq!(value["inputType"], "chat.Message");
2924 assert_eq!(value["outputType"], "chat.Message");
2925 assert_eq!(value["clientStreaming"], true);
2926 assert_eq!(value["serverStreaming"], true);
2927 }
2928
2929 #[test]
2930 fn test_grpc_services_empty_array() {
2931 let services: Vec<GrpcService> = serde_json::from_str("[]").unwrap();
2932 assert!(services.is_empty());
2933 }
2934
2935 #[test]
2936 fn test_grpc_service_round_trips() {
2937 let original = GrpcService {
2938 name: "helloworld.Greeter".into(),
2939 methods: vec![GrpcMethod {
2940 name: "SayHello".into(),
2941 input_type: "helloworld.HelloRequest".into(),
2942 output_type: "helloworld.HelloReply".into(),
2943 client_streaming: false,
2944 server_streaming: false,
2945 }],
2946 };
2947 let json = serde_json::to_string(&original).unwrap();
2948 let parsed: GrpcService = serde_json::from_str(&json).unwrap();
2949 assert_eq!(original, parsed);
2950 }
2951}