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, PartialEq, Eq)]
2051pub struct PactVerification {
2052 pub passed: bool,
2054 pub report: String,
2056}
2057
2058#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2076pub enum MockMode {
2077 Simulate,
2079 Spy,
2081 Capture,
2083}
2084
2085impl MockMode {
2086 pub fn as_str(&self) -> &'static str {
2088 match self {
2089 MockMode::Simulate => "SIMULATE",
2090 MockMode::Spy => "SPY",
2091 MockMode::Capture => "CAPTURE",
2092 }
2093 }
2094
2095 pub fn proxy_unmatched_requests(&self) -> bool {
2098 !matches!(self, MockMode::Simulate)
2099 }
2100}
2101
2102impl std::fmt::Display for MockMode {
2103 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2104 f.write_str(self.as_str())
2105 }
2106}
2107
2108impl std::str::FromStr for MockMode {
2109 type Err = String;
2110
2111 fn from_str(value: &str) -> std::result::Result<Self, Self::Err> {
2114 match value.trim().to_uppercase().as_str() {
2115 "" => Err("mode is required (one of SIMULATE, SPY, CAPTURE)".to_string()),
2116 "SIMULATE" => Ok(MockMode::Simulate),
2117 "SPY" => Ok(MockMode::Spy),
2118 "CAPTURE" => Ok(MockMode::Capture),
2119 other => Err(format!(
2120 "unknown mode '{other}' (expected one of SIMULATE, SPY, CAPTURE)"
2121 )),
2122 }
2123 }
2124}
2125
2126#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
2138#[serde(rename_all = "camelCase")]
2139pub struct GrpcMethod {
2140 pub name: String,
2142
2143 pub input_type: String,
2145
2146 pub output_type: String,
2148
2149 pub client_streaming: bool,
2151
2152 pub server_streaming: bool,
2154}
2155
2156#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
2163#[serde(rename_all = "camelCase")]
2164pub struct GrpcService {
2165 pub name: String,
2167
2168 pub methods: Vec<GrpcMethod>,
2170}
2171
2172#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
2181#[serde(rename_all = "camelCase")]
2182pub struct SocketAddress {
2183 pub host: String,
2185
2186 pub port: u16,
2188
2189 #[serde(skip_serializing_if = "Option::is_none")]
2192 pub scheme: Option<String>,
2193}
2194
2195impl SocketAddress {
2196 pub fn new(host: impl Into<String>, port: u16) -> Self {
2198 Self {
2199 host: host.into(),
2200 port,
2201 scheme: None,
2202 }
2203 }
2204
2205 pub fn scheme(mut self, scheme: impl Into<String>) -> Self {
2207 self.scheme = Some(scheme.into());
2208 self
2209 }
2210
2211 pub fn https(host: impl Into<String>, port: u16) -> Self {
2213 Self::new(host, port).scheme("HTTPS")
2214 }
2215}
2216
2217#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
2226#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
2227pub enum RampCurve {
2228 Linear,
2230 Quadratic,
2232 Exponential,
2234}
2235
2236#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
2242#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
2243pub enum LoadStageType {
2244 Vu,
2246 Rate,
2248 Pause,
2250}
2251
2252#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2261#[serde(rename_all = "camelCase")]
2262pub struct LoadStage {
2263 #[serde(rename = "type")]
2265 pub stage_type: LoadStageType,
2266
2267 pub duration_millis: u64,
2269
2270 #[serde(skip_serializing_if = "Option::is_none")]
2272 pub curve: Option<RampCurve>,
2273
2274 #[serde(skip_serializing_if = "Option::is_none")]
2276 pub vus: Option<u32>,
2277
2278 #[serde(skip_serializing_if = "Option::is_none")]
2280 pub start_vus: Option<u32>,
2281
2282 #[serde(skip_serializing_if = "Option::is_none")]
2284 pub end_vus: Option<u32>,
2285
2286 #[serde(skip_serializing_if = "Option::is_none")]
2288 pub rate: Option<f64>,
2289
2290 #[serde(skip_serializing_if = "Option::is_none")]
2292 pub start_rate: Option<f64>,
2293
2294 #[serde(skip_serializing_if = "Option::is_none")]
2296 pub end_rate: Option<f64>,
2297
2298 #[serde(skip_serializing_if = "Option::is_none")]
2300 pub max_vus: Option<u32>,
2301}
2302
2303impl LoadStage {
2304 fn base(stage_type: LoadStageType, duration_millis: u64) -> Self {
2305 Self {
2306 stage_type,
2307 duration_millis,
2308 curve: None,
2309 vus: None,
2310 start_vus: None,
2311 end_vus: None,
2312 rate: None,
2313 start_rate: None,
2314 end_rate: None,
2315 max_vus: None,
2316 }
2317 }
2318
2319 pub fn vu_hold(vus: u32, duration_millis: u64) -> Self {
2321 let mut stage = Self::base(LoadStageType::Vu, duration_millis);
2322 stage.vus = Some(vus);
2323 stage
2324 }
2325
2326 pub fn vu_ramp(start_vus: u32, end_vus: u32, duration_millis: u64, curve: RampCurve) -> Self {
2329 let mut stage = Self::base(LoadStageType::Vu, duration_millis);
2330 stage.start_vus = Some(start_vus);
2331 stage.end_vus = Some(end_vus);
2332 stage.curve = Some(curve);
2333 stage
2334 }
2335
2336 pub fn rate_hold(rate: f64, duration_millis: u64) -> Self {
2338 let mut stage = Self::base(LoadStageType::Rate, duration_millis);
2339 stage.rate = Some(rate);
2340 stage
2341 }
2342
2343 pub fn rate_ramp(
2346 start_rate: f64,
2347 end_rate: f64,
2348 duration_millis: u64,
2349 curve: RampCurve,
2350 ) -> Self {
2351 let mut stage = Self::base(LoadStageType::Rate, duration_millis);
2352 stage.start_rate = Some(start_rate);
2353 stage.end_rate = Some(end_rate);
2354 stage.curve = Some(curve);
2355 stage
2356 }
2357
2358 pub fn pause(duration_millis: u64) -> Self {
2360 Self::base(LoadStageType::Pause, duration_millis)
2361 }
2362
2363 pub fn max_vus(mut self, max_vus: u32) -> Self {
2365 self.max_vus = Some(max_vus);
2366 self
2367 }
2368}
2369
2370#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
2373#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
2374pub enum LoadShapeType {
2375 Spike,
2377 Stairs,
2379 RampHold,
2381}
2382
2383#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
2388#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
2389pub enum LoadShapeMetric {
2390 Vu,
2392 Rate,
2394}
2395
2396#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2405#[serde(rename_all = "camelCase")]
2406pub struct LoadShape {
2407 #[serde(rename = "type")]
2409 pub shape_type: LoadShapeType,
2410
2411 #[serde(skip_serializing_if = "Option::is_none")]
2413 pub metric: Option<LoadShapeMetric>,
2414
2415 #[serde(skip_serializing_if = "Option::is_none")]
2417 pub curve: Option<RampCurve>,
2418
2419 #[serde(skip_serializing_if = "Option::is_none")]
2421 pub baseline: Option<f64>,
2422
2423 #[serde(skip_serializing_if = "Option::is_none")]
2425 pub peak: Option<f64>,
2426
2427 #[serde(skip_serializing_if = "Option::is_none")]
2429 pub ramp_up_millis: Option<u64>,
2430
2431 #[serde(skip_serializing_if = "Option::is_none")]
2434 pub hold_millis: Option<u64>,
2435
2436 #[serde(skip_serializing_if = "Option::is_none")]
2438 pub ramp_down_millis: Option<u64>,
2439
2440 #[serde(skip_serializing_if = "Option::is_none")]
2442 pub recovery_hold_millis: Option<u64>,
2443
2444 #[serde(skip_serializing_if = "Option::is_none")]
2446 pub start: Option<f64>,
2447
2448 #[serde(skip_serializing_if = "Option::is_none")]
2450 pub step: Option<f64>,
2451
2452 #[serde(skip_serializing_if = "Option::is_none")]
2454 pub steps: Option<u32>,
2455
2456 #[serde(skip_serializing_if = "Option::is_none")]
2458 pub step_duration_millis: Option<u64>,
2459
2460 #[serde(skip_serializing_if = "Option::is_none")]
2462 pub target: Option<f64>,
2463
2464 #[serde(skip_serializing_if = "Option::is_none")]
2466 pub ramp_millis: Option<u64>,
2467}
2468
2469impl LoadShape {
2470 fn base(shape_type: LoadShapeType) -> Self {
2471 Self {
2472 shape_type,
2473 metric: None,
2474 curve: None,
2475 baseline: None,
2476 peak: None,
2477 ramp_up_millis: None,
2478 hold_millis: None,
2479 ramp_down_millis: None,
2480 recovery_hold_millis: None,
2481 start: None,
2482 step: None,
2483 steps: None,
2484 step_duration_millis: None,
2485 target: None,
2486 ramp_millis: None,
2487 }
2488 }
2489
2490 pub fn spike(
2493 baseline: f64,
2494 peak: f64,
2495 ramp_up_millis: u64,
2496 hold_millis: u64,
2497 ramp_down_millis: u64,
2498 ) -> Self {
2499 let mut shape = Self::base(LoadShapeType::Spike);
2500 shape.baseline = Some(baseline);
2501 shape.peak = Some(peak);
2502 shape.ramp_up_millis = Some(ramp_up_millis);
2503 shape.hold_millis = Some(hold_millis);
2504 shape.ramp_down_millis = Some(ramp_down_millis);
2505 shape
2506 }
2507
2508 pub fn stairs(start: f64, step: f64, steps: u32, step_duration_millis: u64) -> Self {
2511 let mut shape = Self::base(LoadShapeType::Stairs);
2512 shape.start = Some(start);
2513 shape.step = Some(step);
2514 shape.steps = Some(steps);
2515 shape.step_duration_millis = Some(step_duration_millis);
2516 shape
2517 }
2518
2519 pub fn ramp_hold(target: f64, ramp_millis: u64, hold_millis: u64) -> Self {
2522 let mut shape = Self::base(LoadShapeType::RampHold);
2523 shape.target = Some(target);
2524 shape.ramp_millis = Some(ramp_millis);
2525 shape.hold_millis = Some(hold_millis);
2526 shape
2527 }
2528
2529 pub fn metric(mut self, metric: LoadShapeMetric) -> Self {
2531 self.metric = Some(metric);
2532 self
2533 }
2534
2535 pub fn curve(mut self, curve: RampCurve) -> Self {
2537 self.curve = Some(curve);
2538 self
2539 }
2540
2541 pub fn recovery_hold_millis(mut self, recovery_hold_millis: u64) -> Self {
2544 self.recovery_hold_millis = Some(recovery_hold_millis);
2545 self
2546 }
2547}
2548
2549#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
2552#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
2553pub enum LoadThresholdMetric {
2554 LatencyP50,
2556 LatencyP95,
2558 LatencyP99,
2560 LatencyP999,
2562 ErrorRate,
2564 ThroughputRps,
2566}
2567
2568#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
2571#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
2572pub enum LoadComparator {
2573 LessThan,
2575 LessThanOrEqual,
2577 GreaterThan,
2579 GreaterThanOrEqual,
2581}
2582
2583#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2587#[serde(rename_all = "camelCase")]
2588pub struct LoadThreshold {
2589 pub metric: LoadThresholdMetric,
2591
2592 pub comparator: LoadComparator,
2594
2595 pub threshold: f64,
2598}
2599
2600impl LoadThreshold {
2601 pub fn new(metric: LoadThresholdMetric, comparator: LoadComparator, threshold: f64) -> Self {
2603 Self {
2604 metric,
2605 comparator,
2606 threshold,
2607 }
2608 }
2609}
2610
2611#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
2614#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
2615pub enum LoadPacingMode {
2616 None,
2618 ConstantPacing,
2620 ConstantThroughput,
2622}
2623
2624#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2628#[serde(rename_all = "camelCase")]
2629pub struct LoadPacing {
2630 pub mode: LoadPacingMode,
2632
2633 pub value: f64,
2637}
2638
2639impl LoadPacing {
2640 pub fn new(mode: LoadPacingMode, value: f64) -> Self {
2642 Self { mode, value }
2643 }
2644
2645 pub fn constant_pacing(cycle_millis: f64) -> Self {
2647 Self::new(LoadPacingMode::ConstantPacing, cycle_millis)
2648 }
2649
2650 pub fn constant_throughput(iterations_per_second: f64) -> Self {
2652 Self::new(LoadPacingMode::ConstantThroughput, iterations_per_second)
2653 }
2654}
2655
2656#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
2659#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
2660pub enum LoadFeederFormat {
2661 Csv,
2663 Json,
2665}
2666
2667#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
2670#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
2671pub enum LoadFeederStrategy {
2672 Circular,
2674 Random,
2676 Sequential,
2678}
2679
2680#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
2685#[serde(rename_all = "camelCase")]
2686pub struct LoadFeeder {
2687 #[serde(skip_serializing_if = "Vec::is_empty")]
2689 pub rows: Vec<HashMap<String, String>>,
2690
2691 #[serde(skip_serializing_if = "Option::is_none")]
2693 pub data: Option<String>,
2694
2695 #[serde(skip_serializing_if = "Option::is_none")]
2697 pub format: Option<LoadFeederFormat>,
2698
2699 #[serde(skip_serializing_if = "Option::is_none")]
2701 pub strategy: Option<LoadFeederStrategy>,
2702}
2703
2704impl LoadFeeder {
2705 pub fn rows(rows: Vec<HashMap<String, String>>) -> Self {
2707 Self {
2708 rows,
2709 ..Self::default()
2710 }
2711 }
2712
2713 pub fn data(data: impl Into<String>, format: LoadFeederFormat) -> Self {
2715 Self {
2716 data: Some(data.into()),
2717 format: Some(format),
2718 ..Self::default()
2719 }
2720 }
2721
2722 pub fn strategy(mut self, strategy: LoadFeederStrategy) -> Self {
2724 self.strategy = Some(strategy);
2725 self
2726 }
2727}
2728
2729#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
2732#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
2733pub enum LoadCaptureSource {
2734 BodyJsonpath,
2736 Header,
2738 BodyRegex,
2740}
2741
2742#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2747#[serde(rename_all = "camelCase")]
2748pub struct LoadCapture {
2749 pub name: String,
2751
2752 pub source: LoadCaptureSource,
2754
2755 pub expression: String,
2757
2758 #[serde(skip_serializing_if = "Option::is_none")]
2760 pub default_value: Option<String>,
2761}
2762
2763impl LoadCapture {
2764 pub fn new(
2767 name: impl Into<String>,
2768 source: LoadCaptureSource,
2769 expression: impl Into<String>,
2770 ) -> Self {
2771 Self {
2772 name: name.into(),
2773 source,
2774 expression: expression.into(),
2775 default_value: None,
2776 }
2777 }
2778
2779 pub fn default_value(mut self, default_value: impl Into<String>) -> Self {
2781 self.default_value = Some(default_value.into());
2782 self
2783 }
2784}
2785
2786#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
2789#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
2790pub enum LoadStepSelection {
2791 Sequential,
2793 Weighted,
2795}
2796
2797#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
2805#[serde(rename_all = "camelCase")]
2806pub struct LoadProfile {
2807 #[serde(skip_serializing_if = "Vec::is_empty")]
2810 pub stages: Vec<LoadStage>,
2811
2812 #[serde(skip_serializing_if = "Option::is_none")]
2815 pub shape: Option<LoadShape>,
2816}
2817
2818impl LoadProfile {
2819 pub fn of(stages: Vec<LoadStage>) -> Self {
2821 Self {
2822 stages,
2823 shape: None,
2824 }
2825 }
2826
2827 pub fn shaped(shape: LoadShape) -> Self {
2829 Self {
2830 stages: Vec::new(),
2831 shape: Some(shape),
2832 }
2833 }
2834
2835 pub fn constant(vus: u32, duration_millis: u64) -> Self {
2837 Self::of(vec![LoadStage::vu_hold(vus, duration_millis)])
2838 }
2839
2840 pub fn linear(start_vus: u32, end_vus: u32, duration_millis: u64) -> Self {
2843 Self::of(vec![LoadStage::vu_ramp(
2844 start_vus,
2845 end_vus,
2846 duration_millis,
2847 RampCurve::Linear,
2848 )])
2849 }
2850
2851 pub fn add_stage(mut self, stage: LoadStage) -> Self {
2853 self.stages.push(stage);
2854 self
2855 }
2856}
2857
2858#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2861#[serde(rename_all = "camelCase")]
2862pub struct LoadStep {
2863 pub request: HttpRequest,
2865
2866 #[serde(skip_serializing_if = "Option::is_none")]
2868 pub think_time: Option<Delay>,
2869
2870 #[serde(skip_serializing_if = "Vec::is_empty")]
2874 pub captures: Vec<LoadCapture>,
2875
2876 #[serde(skip_serializing_if = "Option::is_none")]
2880 pub weight: Option<f64>,
2881}
2882
2883impl LoadStep {
2884 pub fn new(request: HttpRequest) -> Self {
2886 Self {
2887 request,
2888 think_time: None,
2889 captures: Vec::new(),
2890 weight: None,
2891 }
2892 }
2893
2894 pub fn think_time(mut self, delay: Delay) -> Self {
2896 self.think_time = Some(delay);
2897 self
2898 }
2899
2900 pub fn capture(mut self, capture: LoadCapture) -> Self {
2902 self.captures.push(capture);
2903 self
2904 }
2905
2906 pub fn weight(mut self, weight: f64) -> Self {
2909 self.weight = Some(weight);
2910 self
2911 }
2912}
2913
2914#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2921#[serde(rename_all = "camelCase")]
2922pub struct LoadScenario {
2923 pub name: String,
2925
2926 #[serde(skip_serializing_if = "Option::is_none")]
2929 pub template_type: Option<String>,
2930
2931 #[serde(skip_serializing_if = "Option::is_none")]
2933 pub max_requests: Option<u64>,
2934
2935 #[serde(skip_serializing_if = "Option::is_none")]
2939 pub start_delay_millis: Option<u64>,
2940
2941 #[serde(skip_serializing_if = "Vec::is_empty")]
2944 pub thresholds: Vec<LoadThreshold>,
2945
2946 #[serde(skip_serializing_if = "std::ops::Not::not")]
2948 pub abort_on_fail: bool,
2949
2950 #[serde(skip_serializing_if = "Option::is_none")]
2953 pub abort_grace_millis: Option<u64>,
2954
2955 #[serde(skip_serializing_if = "Option::is_none")]
2957 pub pacing: Option<LoadPacing>,
2958
2959 #[serde(skip_serializing_if = "Option::is_none")]
2961 pub feeder: Option<LoadFeeder>,
2962
2963 #[serde(skip_serializing_if = "Option::is_none")]
2966 pub step_selection: Option<LoadStepSelection>,
2967
2968 pub profile: LoadProfile,
2970
2971 pub steps: Vec<LoadStep>,
2973}
2974
2975impl LoadScenario {
2976 pub fn new(name: impl Into<String>, profile: LoadProfile, steps: Vec<LoadStep>) -> Self {
2978 Self {
2979 name: name.into(),
2980 template_type: None,
2981 max_requests: None,
2982 start_delay_millis: None,
2983 thresholds: Vec::new(),
2984 abort_on_fail: false,
2985 abort_grace_millis: None,
2986 pacing: None,
2987 feeder: None,
2988 step_selection: None,
2989 profile,
2990 steps,
2991 }
2992 }
2993
2994 pub fn threshold(mut self, threshold: LoadThreshold) -> Self {
2996 self.thresholds.push(threshold);
2997 self
2998 }
2999
3000 pub fn abort_on_fail(mut self, abort_on_fail: bool) -> Self {
3002 self.abort_on_fail = abort_on_fail;
3003 self
3004 }
3005
3006 pub fn abort_grace_millis(mut self, abort_grace_millis: u64) -> Self {
3008 self.abort_grace_millis = Some(abort_grace_millis);
3009 self
3010 }
3011
3012 pub fn pacing(mut self, pacing: LoadPacing) -> Self {
3014 self.pacing = Some(pacing);
3015 self
3016 }
3017
3018 pub fn feeder(mut self, feeder: LoadFeeder) -> Self {
3020 self.feeder = Some(feeder);
3021 self
3022 }
3023
3024 pub fn step_selection(mut self, step_selection: LoadStepSelection) -> Self {
3026 self.step_selection = Some(step_selection);
3027 self
3028 }
3029
3030 pub fn template_type(mut self, template_type: impl Into<String>) -> Self {
3032 self.template_type = Some(template_type.into());
3033 self
3034 }
3035
3036 pub fn max_requests(mut self, max_requests: u64) -> Self {
3038 self.max_requests = Some(max_requests);
3039 self
3040 }
3041
3042 pub fn start_delay_millis(mut self, start_delay_millis: u64) -> Self {
3045 self.start_delay_millis = Some(start_delay_millis);
3046 self
3047 }
3048}
3049
3050#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
3057#[serde(rename_all = "camelCase")]
3058pub struct SloObjective {
3059 pub sli: String,
3062
3063 pub comparator: String,
3067
3068 pub threshold: f64,
3071
3072 #[serde(skip_serializing_if = "Option::is_none")]
3075 pub scope: Option<String>,
3076}
3077
3078impl SloObjective {
3079 pub fn new(sli: impl Into<String>, comparator: impl Into<String>, threshold: f64) -> Self {
3081 Self {
3082 sli: sli.into(),
3083 comparator: comparator.into(),
3084 threshold,
3085 scope: None,
3086 }
3087 }
3088
3089 pub fn scope(mut self, scope: impl Into<String>) -> Self {
3091 self.scope = Some(scope.into());
3092 self
3093 }
3094}
3095
3096#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
3099#[serde(rename_all = "camelCase")]
3100pub struct SloWindow {
3101 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
3103 pub window_type: Option<String>,
3104
3105 #[serde(skip_serializing_if = "Option::is_none")]
3107 pub lookback_millis: Option<u64>,
3108
3109 #[serde(skip_serializing_if = "Option::is_none")]
3111 pub from_epoch_millis: Option<u64>,
3112
3113 #[serde(skip_serializing_if = "Option::is_none")]
3115 pub to_epoch_millis: Option<u64>,
3116}
3117
3118impl SloWindow {
3119 pub fn lookback(millis: u64) -> Self {
3121 Self {
3122 window_type: Some("LOOKBACK".to_string()),
3123 lookback_millis: Some(millis),
3124 ..Default::default()
3125 }
3126 }
3127
3128 pub fn explicit(from_epoch_millis: u64, to_epoch_millis: u64) -> Self {
3130 Self {
3131 window_type: Some("EXPLICIT".to_string()),
3132 from_epoch_millis: Some(from_epoch_millis),
3133 to_epoch_millis: Some(to_epoch_millis),
3134 ..Default::default()
3135 }
3136 }
3137}
3138
3139#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
3142#[serde(rename_all = "camelCase")]
3143pub struct SloCriteria {
3144 #[serde(skip_serializing_if = "Option::is_none")]
3146 pub name: Option<String>,
3147
3148 #[serde(skip_serializing_if = "Option::is_none")]
3150 pub window: Option<SloWindow>,
3151
3152 #[serde(skip_serializing_if = "Option::is_none")]
3155 pub minimum_sample_count: Option<u64>,
3156
3157 #[serde(skip_serializing_if = "Option::is_none")]
3159 pub upstream_hosts: Option<Vec<String>>,
3160
3161 pub objectives: Vec<SloObjective>,
3163}
3164
3165impl SloCriteria {
3166 pub fn new(objectives: Vec<SloObjective>) -> Self {
3168 Self {
3169 name: None,
3170 window: None,
3171 minimum_sample_count: None,
3172 upstream_hosts: None,
3173 objectives,
3174 }
3175 }
3176
3177 pub fn name(mut self, name: impl Into<String>) -> Self {
3179 self.name = Some(name.into());
3180 self
3181 }
3182
3183 pub fn window(mut self, window: SloWindow) -> Self {
3185 self.window = Some(window);
3186 self
3187 }
3188
3189 pub fn minimum_sample_count(mut self, count: u64) -> Self {
3191 self.minimum_sample_count = Some(count);
3192 self
3193 }
3194
3195 pub fn upstream_hosts(mut self, hosts: Vec<String>) -> Self {
3197 self.upstream_hosts = Some(hosts);
3198 self
3199 }
3200}
3201
3202#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
3205#[serde(rename_all = "camelCase")]
3206pub struct SloObjectiveResult {
3207 #[serde(skip_serializing_if = "Option::is_none")]
3208 pub sli: Option<String>,
3209 #[serde(skip_serializing_if = "Option::is_none")]
3210 pub comparator: Option<String>,
3211 #[serde(skip_serializing_if = "Option::is_none")]
3212 pub threshold: Option<f64>,
3213 #[serde(skip_serializing_if = "Option::is_none")]
3214 pub observed_value: Option<f64>,
3215 #[serde(skip_serializing_if = "Option::is_none")]
3217 pub result: Option<String>,
3218 #[serde(skip_serializing_if = "Option::is_none")]
3219 pub detail: Option<String>,
3220}
3221
3222#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
3225#[serde(rename_all = "camelCase")]
3226pub struct SloVerdict {
3227 #[serde(skip_serializing_if = "Option::is_none")]
3228 pub name: Option<String>,
3229 #[serde(skip_serializing_if = "Option::is_none")]
3231 pub result: Option<String>,
3232 #[serde(skip_serializing_if = "Option::is_none")]
3233 pub window_from_epoch_millis: Option<u64>,
3234 #[serde(skip_serializing_if = "Option::is_none")]
3235 pub window_to_epoch_millis: Option<u64>,
3236 #[serde(skip_serializing_if = "Option::is_none")]
3237 pub sample_count: Option<u64>,
3238 #[serde(default, skip_serializing_if = "Vec::is_empty")]
3239 pub objective_results: Vec<SloObjectiveResult>,
3240}
3241
3242impl SloVerdict {
3243 pub fn is_pass(&self) -> bool {
3245 self.result.as_deref() == Some("PASS")
3246 }
3247
3248 pub fn is_fail(&self) -> bool {
3250 self.result.as_deref() == Some("FAIL")
3251 }
3252
3253 pub fn is_inconclusive(&self) -> bool {
3255 self.result.as_deref() == Some("INCONCLUSIVE")
3256 }
3257}
3258
3259#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
3266#[serde(rename_all = "camelCase")]
3267pub struct PreemptionRequest {
3268 #[serde(skip_serializing_if = "Option::is_none")]
3271 pub mode: Option<String>,
3272
3273 #[serde(skip_serializing_if = "Option::is_none")]
3275 pub drain_millis: Option<u64>,
3276
3277 #[serde(skip_serializing_if = "Option::is_none")]
3280 pub ttl_millis: Option<u64>,
3281
3282 #[serde(skip_serializing_if = "Option::is_none")]
3285 pub last_stream_id: Option<i64>,
3286}
3287
3288impl PreemptionRequest {
3289 pub fn new() -> Self {
3291 Self::default()
3292 }
3293
3294 pub fn mode(mut self, mode: impl Into<String>) -> Self {
3296 self.mode = Some(mode.into());
3297 self
3298 }
3299
3300 pub fn drain_millis(mut self, millis: u64) -> Self {
3302 self.drain_millis = Some(millis);
3303 self
3304 }
3305
3306 pub fn ttl_millis(mut self, millis: u64) -> Self {
3308 self.ttl_millis = Some(millis);
3309 self
3310 }
3311
3312 pub fn last_stream_id(mut self, id: i64) -> Self {
3314 self.last_stream_id = Some(id);
3315 self
3316 }
3317}
3318
3319#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
3322#[serde(rename_all = "camelCase")]
3323pub struct PreemptionStatus {
3324 #[serde(skip_serializing_if = "Option::is_none")]
3326 pub state: Option<String>,
3327
3328 #[serde(skip_serializing_if = "Option::is_none")]
3330 pub in_flight: Option<u64>,
3331
3332 #[serde(skip_serializing_if = "Option::is_none")]
3334 pub drain_remaining_millis: Option<u64>,
3335
3336 #[serde(skip_serializing_if = "Option::is_none")]
3338 pub mode: Option<String>,
3339}
3340
3341#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
3349#[serde(rename_all = "camelCase")]
3350pub struct HttpChaosProfile {
3351 #[serde(skip_serializing_if = "Option::is_none")]
3353 pub error_status: Option<u16>,
3354
3355 #[serde(skip_serializing_if = "Option::is_none")]
3357 pub error_probability: Option<f64>,
3358
3359 #[serde(skip_serializing_if = "Option::is_none")]
3361 pub latency: Option<Delay>,
3362
3363 #[serde(skip_serializing_if = "Option::is_none")]
3365 pub connection_drop: Option<bool>,
3366
3367 #[serde(skip_serializing_if = "Option::is_none")]
3369 pub seed: Option<i64>,
3370
3371 #[serde(flatten)]
3373 pub extra: HashMap<String, serde_json::Value>,
3374}
3375
3376impl HttpChaosProfile {
3377 pub fn new() -> Self {
3379 Self::default()
3380 }
3381
3382 pub fn error_status(mut self, status: u16) -> Self {
3384 self.error_status = Some(status);
3385 self
3386 }
3387
3388 pub fn error_probability(mut self, probability: f64) -> Self {
3390 self.error_probability = Some(probability);
3391 self
3392 }
3393
3394 pub fn latency(mut self, latency: Delay) -> Self {
3396 self.latency = Some(latency);
3397 self
3398 }
3399
3400 pub fn connection_drop(mut self, drop: bool) -> Self {
3402 self.connection_drop = Some(drop);
3403 self
3404 }
3405
3406 pub fn seed(mut self, seed: i64) -> Self {
3408 self.seed = Some(seed);
3409 self
3410 }
3411}
3412
3413#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
3420#[serde(rename_all = "camelCase")]
3421pub struct ChaosStage {
3422 pub duration_millis: u64,
3424
3425 pub profiles: HashMap<String, HttpChaosProfile>,
3427}
3428
3429impl ChaosStage {
3430 pub fn new(duration_millis: u64) -> Self {
3432 Self {
3433 duration_millis,
3434 profiles: HashMap::new(),
3435 }
3436 }
3437
3438 pub fn profile(mut self, host: impl Into<String>, profile: HttpChaosProfile) -> Self {
3440 self.profiles.insert(host.into(), profile);
3441 self
3442 }
3443}
3444
3445#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
3448#[serde(rename_all = "camelCase")]
3449pub struct ChaosExperiment {
3450 #[serde(skip_serializing_if = "Option::is_none")]
3452 pub name: Option<String>,
3453
3454 #[serde(rename = "loop", skip_serializing_if = "Option::is_none")]
3457 pub loop_back: Option<bool>,
3458
3459 pub stages: Vec<ChaosStage>,
3461}
3462
3463impl ChaosExperiment {
3464 pub fn new(stages: Vec<ChaosStage>) -> Self {
3466 Self {
3467 name: None,
3468 loop_back: None,
3469 stages,
3470 }
3471 }
3472
3473 pub fn name(mut self, name: impl Into<String>) -> Self {
3475 self.name = Some(name.into());
3476 self
3477 }
3478
3479 pub fn loop_back(mut self, loop_back: bool) -> Self {
3481 self.loop_back = Some(loop_back);
3482 self
3483 }
3484}
3485
3486#[cfg(test)]
3491mod tests {
3492 use super::*;
3493
3494 #[test]
3495 fn test_grpc_services_deserialize_from_server_wire_shape() {
3496 let wire = r#"[
3499 {
3500 "name": "helloworld.Greeter",
3501 "methods": [
3502 {
3503 "name": "SayHello",
3504 "inputType": "helloworld.HelloRequest",
3505 "outputType": "helloworld.HelloReply",
3506 "clientStreaming": false,
3507 "serverStreaming": false
3508 },
3509 {
3510 "name": "LotsOfReplies",
3511 "inputType": "helloworld.HelloRequest",
3512 "outputType": "helloworld.HelloReply",
3513 "clientStreaming": false,
3514 "serverStreaming": true
3515 }
3516 ]
3517 }
3518 ]"#;
3519
3520 let services: Vec<GrpcService> = serde_json::from_str(wire).unwrap();
3521 assert_eq!(services.len(), 1);
3522 let svc = &services[0];
3523 assert_eq!(svc.name, "helloworld.Greeter");
3524 assert_eq!(svc.methods.len(), 2);
3525
3526 let unary = &svc.methods[0];
3527 assert_eq!(unary.name, "SayHello");
3528 assert_eq!(unary.input_type, "helloworld.HelloRequest");
3529 assert_eq!(unary.output_type, "helloworld.HelloReply");
3530 assert!(!unary.client_streaming);
3531 assert!(!unary.server_streaming);
3532
3533 let server_stream = &svc.methods[1];
3534 assert_eq!(server_stream.name, "LotsOfReplies");
3535 assert!(!server_stream.client_streaming);
3536 assert!(server_stream.server_streaming);
3537 }
3538
3539 #[test]
3540 fn test_grpc_method_serializes_with_camel_case_keys() {
3541 let method = GrpcMethod {
3542 name: "BidiChat".into(),
3543 input_type: "chat.Message".into(),
3544 output_type: "chat.Message".into(),
3545 client_streaming: true,
3546 server_streaming: true,
3547 };
3548 let value = serde_json::to_value(&method).unwrap();
3549 assert_eq!(value["name"], "BidiChat");
3550 assert_eq!(value["inputType"], "chat.Message");
3551 assert_eq!(value["outputType"], "chat.Message");
3552 assert_eq!(value["clientStreaming"], true);
3553 assert_eq!(value["serverStreaming"], true);
3554 }
3555
3556 #[test]
3557 fn test_grpc_services_empty_array() {
3558 let services: Vec<GrpcService> = serde_json::from_str("[]").unwrap();
3559 assert!(services.is_empty());
3560 }
3561
3562 #[test]
3563 fn test_grpc_service_round_trips() {
3564 let original = GrpcService {
3565 name: "helloworld.Greeter".into(),
3566 methods: vec![GrpcMethod {
3567 name: "SayHello".into(),
3568 input_type: "helloworld.HelloRequest".into(),
3569 output_type: "helloworld.HelloReply".into(),
3570 client_streaming: false,
3571 server_streaming: false,
3572 }],
3573 };
3574 let json = serde_json::to_string(&original).unwrap();
3575 let parsed: GrpcService = serde_json::from_str(&json).unwrap();
3576 assert_eq!(original, parsed);
3577 }
3578}