1use base64::{engine::general_purpose::STANDARD as BASE64, Engine};
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9
10pub type Extra = serde_json::Map<String, serde_json::Value>;
18
19fn one_or_many<'de, D, T>(deserializer: D) -> std::result::Result<Option<Vec<T>>, D::Error>
23where
24 D: serde::Deserializer<'de>,
25 T: Deserialize<'de>,
26{
27 #[derive(Deserialize)]
28 #[serde(untagged)]
29 enum OneOrMany<T> {
30 One(T),
31 Many(Vec<T>),
32 }
33 let opt = Option::<OneOrMany<T>>::deserialize(deserializer)?;
34 Ok(opt.map(|v| match v {
35 OneOrMany::One(t) => vec![t],
36 OneOrMany::Many(v) => v,
37 }))
38}
39
40#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
59#[serde(untagged)]
60pub enum ParameterValues {
61 Values(Vec<String>),
63 Matcher(serde_json::Value),
65}
66
67impl ParameterValues {
68 pub fn as_values(&self) -> Option<&[String]> {
70 match self {
71 ParameterValues::Values(v) => Some(v),
72 ParameterValues::Matcher(_) => None,
73 }
74 }
75}
76
77impl From<Vec<String>> for ParameterValues {
78 fn from(values: Vec<String>) -> Self {
79 ParameterValues::Values(values)
80 }
81}
82
83#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
101#[serde(rename_all = "camelCase")]
102pub struct HttpRequest {
103 #[serde(skip_serializing_if = "Option::is_none")]
104 pub method: Option<String>,
105
106 #[serde(skip_serializing_if = "Option::is_none")]
107 pub path: Option<String>,
108
109 #[serde(skip_serializing_if = "Option::is_none")]
110 pub query_string_parameters: Option<HashMap<String, Vec<String>>>,
111
112 #[serde(skip_serializing_if = "Option::is_none")]
113 pub headers: Option<HashMap<String, Vec<String>>>,
114
115 #[serde(skip_serializing_if = "Option::is_none")]
116 pub body: Option<Body>,
117
118 #[serde(skip_serializing_if = "Option::is_none")]
119 pub jwt: Option<Jwt>,
120
121 #[serde(skip_serializing_if = "Option::is_none")]
122 pub socket_address: Option<SocketAddress>,
123
124 #[serde(skip_serializing_if = "Option::is_none")]
126 pub not: Option<bool>,
127
128 #[serde(skip_serializing_if = "Option::is_none")]
130 pub secure: Option<bool>,
131
132 #[serde(skip_serializing_if = "Option::is_none")]
134 pub keep_alive: Option<bool>,
135
136 #[serde(skip_serializing_if = "Option::is_none")]
138 pub protocol: Option<String>,
139
140 #[serde(skip_serializing_if = "Option::is_none")]
145 pub path_parameters: Option<HashMap<String, ParameterValues>>,
146
147 #[serde(skip_serializing_if = "Option::is_none")]
149 pub cookies: Option<HashMap<String, String>>,
150
151 #[serde(flatten, default)]
154 pub extra: Extra,
155}
156
157impl HttpRequest {
158 pub fn new() -> Self {
160 Self::default()
161 }
162
163 pub fn socket_address(mut self, socket_address: SocketAddress) -> Self {
169 self.socket_address = Some(socket_address);
170 self
171 }
172
173 pub fn method(mut self, method: impl Into<String>) -> Self {
175 self.method = Some(method.into());
176 self
177 }
178
179 pub fn path(mut self, path: impl Into<String>) -> Self {
181 self.path = Some(path.into());
182 self
183 }
184
185 pub fn query_param(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
187 let params = self
188 .query_string_parameters
189 .get_or_insert_with(HashMap::new);
190 params.entry(key.into()).or_default().push(value.into());
191 self
192 }
193
194 pub fn header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
196 let headers = self.headers.get_or_insert_with(HashMap::new);
197 headers.entry(key.into()).or_default().push(value.into());
198 self
199 }
200
201 pub fn body(mut self, body: impl Into<String>) -> Self {
203 self.body = Some(Body::Plain(body.into()));
204 self
205 }
206
207 pub fn json_body(mut self, json: serde_json::Value) -> Self {
209 self.body = Some(Body::Typed {
210 body_type: "JSON".to_string(),
211 json: json.to_string(),
212 });
213 self
214 }
215
216 pub fn file_body(mut self, file_path: impl Into<String>) -> Self {
221 self.body = Some(Body::File {
222 file_path: file_path.into(),
223 content_type: None,
224 template_type: None,
225 });
226 self
227 }
228
229 pub fn body_value(mut self, body: Body) -> Self {
231 self.body = Some(body);
232 self
233 }
234
235 pub fn jwt(mut self, jwt: Jwt) -> Self {
255 self.jwt = Some(jwt);
256 self
257 }
258
259 pub fn not(mut self, not: bool) -> Self {
261 self.not = Some(not);
262 self
263 }
264
265 pub fn secure(mut self, secure: bool) -> Self {
267 self.secure = Some(secure);
268 self
269 }
270
271 pub fn keep_alive(mut self, keep_alive: bool) -> Self {
273 self.keep_alive = Some(keep_alive);
274 self
275 }
276
277 pub fn protocol(mut self, protocol: impl Into<String>) -> Self {
279 self.protocol = Some(protocol.into());
280 self
281 }
282
283 pub fn path_param(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
288 let params = self.path_parameters.get_or_insert_with(HashMap::new);
289 match params
290 .entry(key.into())
291 .or_insert_with(|| ParameterValues::Values(Vec::new()))
292 {
293 ParameterValues::Values(v) => v.push(value.into()),
294 ParameterValues::Matcher(_) => {
295 }
298 }
299 self
300 }
301
302 pub fn cookie(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
304 let cookies = self.cookies.get_or_insert_with(HashMap::new);
305 cookies.insert(name.into(), value.into());
306 self
307 }
308}
309
310#[derive(Debug, Clone, PartialEq)]
316pub enum Body {
317 Plain(String),
319 Typed { body_type: String, json: String },
321 File {
323 file_path: String,
324 content_type: Option<String>,
325 template_type: Option<String>,
326 },
327 AllOf(Vec<Body>),
332 Matcher {
338 body_type: String,
339 value_key: String,
340 value: String,
341 },
342 Object(serde_json::Map<String, serde_json::Value>),
349}
350
351impl Body {
352 pub fn file(file_path: impl Into<String>) -> Self {
363 Body::File {
364 file_path: file_path.into(),
365 content_type: None,
366 template_type: None,
367 }
368 }
369
370 pub fn with_content_type(mut self, content_type: impl Into<String>) -> Self {
372 if let Body::File {
373 content_type: ref mut ct,
374 ..
375 } = self
376 {
377 *ct = Some(content_type.into());
378 }
379 self
380 }
381
382 pub fn with_template_type(mut self, template_type: impl Into<String>) -> Self {
385 if let Body::File {
386 template_type: ref mut tt,
387 ..
388 } = self
389 {
390 *tt = Some(template_type.into());
391 }
392 self
393 }
394
395 pub fn all_of(bodies: Vec<Body>) -> Self {
408 Body::AllOf(bodies)
409 }
410
411 pub fn json_path(expression: impl Into<String>) -> Self {
415 Body::Matcher {
416 body_type: "JSON_PATH".to_string(),
417 value_key: "jsonPath".to_string(),
418 value: expression.into(),
419 }
420 }
421
422 pub fn regex(pattern: impl Into<String>) -> Self {
426 Body::Matcher {
427 body_type: "REGEX".to_string(),
428 value_key: "regex".to_string(),
429 value: pattern.into(),
430 }
431 }
432
433 pub fn xpath(expression: impl Into<String>) -> Self {
435 Body::Matcher {
436 body_type: "XPATH".to_string(),
437 value_key: "xpath".to_string(),
438 value: expression.into(),
439 }
440 }
441
442 pub fn string(value: impl Into<String>, sub_string: bool) -> Self {
447 let mut map = serde_json::Map::new();
448 map.insert("type".into(), serde_json::Value::from("STRING"));
449 map.insert("string".into(), serde_json::Value::from(value.into()));
450 map.insert("subString".into(), serde_json::Value::from(sub_string));
451 Body::Object(map)
452 }
453
454 pub fn xml(value: impl Into<String>) -> Self {
456 Self::single_object("XML", "xml", value.into())
457 }
458
459 pub fn xml_schema(schema: impl Into<String>) -> Self {
462 Self::single_object("XML_SCHEMA", "xmlSchema", schema.into())
463 }
464
465 pub fn json_schema(schema: impl Into<String>) -> Self {
468 Self::single_object("JSON_SCHEMA", "jsonSchema", schema.into())
469 }
470
471 pub fn parameters(parameters: HashMap<String, Vec<String>>) -> Self {
474 let mut map = serde_json::Map::new();
475 map.insert("type".into(), serde_json::Value::from("PARAMETERS"));
476 map.insert(
477 "parameters".into(),
478 serde_json::to_value(parameters).unwrap_or(serde_json::Value::Null),
479 );
480 Body::Object(map)
481 }
482
483 pub fn binary(data: impl AsRef<[u8]>, content_type: Option<String>) -> Self {
486 let mut map = serde_json::Map::new();
487 map.insert("type".into(), serde_json::Value::from("BINARY"));
488 map.insert(
489 "base64Bytes".into(),
490 serde_json::Value::from(BASE64.encode(data.as_ref())),
491 );
492 if let Some(ct) = content_type {
493 map.insert("contentType".into(), serde_json::Value::from(ct));
494 }
495 Body::Object(map)
496 }
497
498 pub fn graphql(query: impl Into<String>) -> Self {
500 let mut map = serde_json::Map::new();
501 map.insert("type".into(), serde_json::Value::from("GRAPHQL"));
502 map.insert("query".into(), serde_json::Value::from(query.into()));
503 Body::Object(map)
504 }
505
506 pub fn wasm(object: serde_json::Map<String, serde_json::Value>) -> Self {
511 Body::Object(object)
512 }
513
514 pub fn object(object: serde_json::Map<String, serde_json::Value>) -> Self {
517 Body::Object(object)
518 }
519
520 fn single_object(body_type: &str, key: &str, value: String) -> Self {
521 let mut map = serde_json::Map::new();
522 map.insert("type".into(), serde_json::Value::from(body_type));
523 map.insert(key.into(), serde_json::Value::from(value));
524 Body::Object(map)
525 }
526}
527
528impl Serialize for Body {
529 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
530 where
531 S: serde::Serializer,
532 {
533 match self {
534 Body::Plain(s) => serializer.serialize_str(s),
535 Body::Typed { body_type, json } => {
536 use serde::ser::SerializeMap;
537 let mut map = serializer.serialize_map(Some(2))?;
538 map.serialize_entry("type", body_type)?;
539 map.serialize_entry("json", json)?;
540 map.end()
541 }
542 Body::File {
543 file_path,
544 content_type,
545 template_type,
546 } => {
547 use serde::ser::SerializeMap;
548 let count = 2
549 + content_type.as_ref().map_or(0, |_| 1)
550 + template_type.as_ref().map_or(0, |_| 1);
551 let mut map = serializer.serialize_map(Some(count))?;
552 map.serialize_entry("type", "FILE")?;
553 map.serialize_entry("filePath", file_path)?;
554 if let Some(ct) = content_type {
555 map.serialize_entry("contentType", ct)?;
556 }
557 if let Some(tt) = template_type {
558 map.serialize_entry("templateType", tt)?;
559 }
560 map.end()
561 }
562 Body::AllOf(bodies) => {
563 use serde::ser::SerializeMap;
564 let mut map = serializer.serialize_map(Some(2))?;
565 map.serialize_entry("type", "ALL_OF")?;
566 map.serialize_entry("bodyAllOf", bodies)?;
567 map.end()
568 }
569 Body::Matcher {
570 body_type,
571 value_key,
572 value,
573 } => {
574 use serde::ser::SerializeMap;
575 let mut map = serializer.serialize_map(Some(2))?;
576 map.serialize_entry("type", body_type)?;
577 map.serialize_entry(value_key.as_str(), value)?;
578 map.end()
579 }
580 Body::Object(object) => object.serialize(serializer),
581 }
582 }
583}
584
585fn matcher_key_value(
588 body_type: &str,
589 map: &serde_json::Map<String, serde_json::Value>,
590) -> Option<(String, String)> {
591 let key = match body_type {
592 "JSON_PATH" => "jsonPath",
593 "REGEX" => "regex",
594 "XPATH" => "xpath",
595 _ => return None,
596 };
597 map.get(key)
598 .and_then(|v| v.as_str())
599 .map(|s| (key.to_string(), s.to_string()))
600}
601
602impl<'de> Deserialize<'de> for Body {
603 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
604 where
605 D: serde::Deserializer<'de>,
606 {
607 use serde_json::Value;
608 let v = Value::deserialize(deserializer)?;
609 match v {
610 Value::String(s) => Ok(Body::Plain(s)),
611 Value::Object(map) => {
612 let body_type = map
613 .get("type")
614 .and_then(|v| v.as_str())
615 .unwrap_or("JSON")
616 .to_string();
617 if body_type == "FILE" {
618 let file_path = map
619 .get("filePath")
620 .and_then(|v| v.as_str())
621 .unwrap_or("")
622 .to_string();
623 let content_type = map
624 .get("contentType")
625 .and_then(|v| v.as_str())
626 .map(|s| s.to_string());
627 let template_type = map
628 .get("templateType")
629 .and_then(|v| v.as_str())
630 .map(|s| s.to_string());
631 Ok(Body::File {
632 file_path,
633 content_type,
634 template_type,
635 })
636 } else if body_type == "ALL_OF" {
637 let bodies = map
638 .get("bodyAllOf")
639 .and_then(|v| v.as_array())
640 .map(|arr| {
641 arr.iter()
642 .cloned()
643 .map(serde_json::from_value)
644 .collect::<std::result::Result<Vec<Body>, _>>()
645 })
646 .transpose()
647 .map_err(serde::de::Error::custom)?
648 .unwrap_or_default();
649 Ok(Body::AllOf(bodies))
650 } else if map.len() == 2 && matcher_key_value(&body_type, &map).is_some() {
651 let (value_key, value) = matcher_key_value(&body_type, &map)
656 .expect("matcher_key_value checked above");
657 Ok(Body::Matcher {
658 body_type,
659 value_key,
660 value,
661 })
662 } else if body_type == "JSON"
663 && map.len() == 2
664 && map.get("json").is_some_and(|v| v.is_string())
665 {
666 let json = map
672 .get("json")
673 .and_then(|v| v.as_str())
674 .unwrap_or("")
675 .to_string();
676 Ok(Body::Typed { body_type, json })
677 } else {
678 Ok(Body::Object(map))
683 }
684 }
685 _ => Ok(Body::Plain(v.to_string())),
686 }
687 }
688}
689
690#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
717#[serde(rename_all = "camelCase")]
718pub struct Jwt {
719 pub claims: HashMap<String, String>,
721
722 #[serde(skip_serializing_if = "Option::is_none")]
723 pub issuer: Option<String>,
724
725 #[serde(skip_serializing_if = "Option::is_none")]
726 pub audience: Option<String>,
727
728 #[serde(skip_serializing_if = "Option::is_none")]
729 pub algorithm: Option<String>,
730
731 #[serde(skip_serializing_if = "Option::is_none")]
732 pub header: Option<String>,
733
734 #[serde(skip_serializing_if = "Option::is_none")]
735 pub scheme: Option<String>,
736}
737
738impl Jwt {
739 pub fn new() -> Self {
741 Self::default()
742 }
743
744 pub fn claim(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
747 self.claims.insert(name.into(), value.into());
748 self
749 }
750
751 pub fn claims(mut self, claims: HashMap<String, String>) -> Self {
753 self.claims = claims;
754 self
755 }
756
757 pub fn issuer(mut self, issuer: impl Into<String>) -> Self {
759 self.issuer = Some(issuer.into());
760 self
761 }
762
763 pub fn audience(mut self, audience: impl Into<String>) -> Self {
765 self.audience = Some(audience.into());
766 self
767 }
768
769 pub fn algorithm(mut self, algorithm: impl Into<String>) -> Self {
771 self.algorithm = Some(algorithm.into());
772 self
773 }
774
775 pub fn header(mut self, header: impl Into<String>) -> Self {
777 self.header = Some(header.into());
778 self
779 }
780
781 pub fn scheme(mut self, scheme: impl Into<String>) -> Self {
783 self.scheme = Some(scheme.into());
784 self
785 }
786}
787
788#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
804#[serde(rename_all = "camelCase")]
805pub struct HttpResponse {
806 #[serde(skip_serializing_if = "Option::is_none")]
807 pub status_code: Option<u16>,
808
809 #[serde(skip_serializing_if = "Option::is_none")]
810 pub headers: Option<HashMap<String, Vec<String>>>,
811
812 #[serde(skip_serializing_if = "Option::is_none")]
813 pub body: Option<String>,
814
815 #[serde(skip_serializing_if = "Option::is_none")]
816 pub delay: Option<Delay>,
817
818 #[serde(skip_serializing_if = "Option::is_none")]
820 pub cookies: Option<HashMap<String, String>>,
821
822 #[serde(skip_serializing_if = "Option::is_none")]
825 pub reason_phrase: Option<String>,
826
827 #[serde(skip_serializing_if = "Option::is_none")]
829 pub status_code_range: Option<String>,
830
831 #[serde(skip_serializing_if = "Option::is_none")]
833 pub trailers: Option<HashMap<String, Vec<String>>>,
834
835 #[serde(skip_serializing_if = "Option::is_none")]
837 pub generate_from_schema: Option<String>,
838
839 #[serde(skip_serializing_if = "Option::is_none")]
841 pub connection_options: Option<ConnectionOptions>,
842
843 #[serde(skip_serializing_if = "Option::is_none")]
845 pub recover_after: Option<RecoverAfter>,
846
847 #[serde(skip_serializing_if = "Option::is_none")]
849 pub primary: Option<bool>,
850
851 #[serde(flatten, default)]
854 pub extra: Extra,
855}
856
857impl HttpResponse {
858 pub fn new() -> Self {
860 Self::default()
861 }
862
863 pub fn status_code(mut self, code: u16) -> Self {
865 self.status_code = Some(code);
866 self
867 }
868
869 pub fn header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
871 let headers = self.headers.get_or_insert_with(HashMap::new);
872 headers.entry(key.into()).or_default().push(value.into());
873 self
874 }
875
876 pub fn body(mut self, body: impl Into<String>) -> Self {
878 self.body = Some(body.into());
879 self
880 }
881
882 pub fn delay(mut self, delay: Delay) -> Self {
884 self.delay = Some(delay);
885 self
886 }
887
888 pub fn cookie(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
890 let cookies = self.cookies.get_or_insert_with(HashMap::new);
891 cookies.insert(name.into(), value.into());
892 self
893 }
894
895 pub fn reason_phrase(mut self, reason_phrase: impl Into<String>) -> Self {
897 self.reason_phrase = Some(reason_phrase.into());
898 self
899 }
900
901 pub fn status_code_range(mut self, range: impl Into<String>) -> Self {
903 self.status_code_range = Some(range.into());
904 self
905 }
906
907 pub fn trailer(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
909 let trailers = self.trailers.get_or_insert_with(HashMap::new);
910 trailers.entry(key.into()).or_default().push(value.into());
911 self
912 }
913
914 pub fn generate_from_schema(mut self, schema: impl Into<String>) -> Self {
916 self.generate_from_schema = Some(schema.into());
917 self
918 }
919
920 pub fn connection_options(mut self, options: ConnectionOptions) -> Self {
922 self.connection_options = Some(options);
923 self
924 }
925
926 pub fn recover_after(mut self, recover_after: RecoverAfter) -> Self {
928 self.recover_after = Some(recover_after);
929 self
930 }
931
932 pub fn primary(mut self, primary: bool) -> Self {
934 self.primary = Some(primary);
935 self
936 }
937}
938
939#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
955#[serde(rename_all = "camelCase")]
956pub struct HttpTemplate {
957 #[serde(skip_serializing_if = "Option::is_none")]
958 pub template_type: Option<String>,
959
960 #[serde(skip_serializing_if = "Option::is_none")]
961 pub template: Option<String>,
962
963 #[serde(skip_serializing_if = "Option::is_none")]
964 pub template_file: Option<String>,
965}
966
967impl HttpTemplate {
968 pub fn new(template_type: impl Into<String>, template: impl Into<String>) -> Self {
970 Self {
971 template_type: Some(template_type.into()),
972 template: Some(template.into()),
973 template_file: None,
974 }
975 }
976
977 pub fn from_file(template_type: impl Into<String>, file_path: impl Into<String>) -> Self {
979 Self {
980 template_type: Some(template_type.into()),
981 template: None,
982 template_file: Some(file_path.into()),
983 }
984 }
985
986 pub fn template_type(mut self, template_type: impl Into<String>) -> Self {
988 self.template_type = Some(template_type.into());
989 self
990 }
991
992 pub fn template(mut self, template: impl Into<String>) -> Self {
994 self.template = Some(template.into());
995 self
996 }
997
998 pub fn template_file(mut self, file_path: impl Into<String>) -> Self {
1000 self.template_file = Some(file_path.into());
1001 self
1002 }
1003}
1004
1005#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1018#[serde(rename_all = "camelCase")]
1019pub struct HttpForward {
1020 pub host: String,
1021
1022 #[serde(skip_serializing_if = "Option::is_none")]
1023 pub port: Option<u16>,
1024
1025 #[serde(skip_serializing_if = "Option::is_none")]
1026 pub scheme: Option<String>,
1027
1028 #[serde(skip_serializing_if = "Option::is_none")]
1030 pub delay: Option<Delay>,
1031}
1032
1033impl HttpForward {
1034 pub fn new(host: impl Into<String>, port: u16) -> Self {
1036 Self {
1037 host: host.into(),
1038 port: Some(port),
1039 scheme: None,
1040 delay: None,
1041 }
1042 }
1043
1044 pub fn scheme(mut self, scheme: impl Into<String>) -> Self {
1046 self.scheme = Some(scheme.into());
1047 self
1048 }
1049
1050 pub fn delay(mut self, delay: Delay) -> Self {
1052 self.delay = Some(delay);
1053 self
1054 }
1055}
1056
1057#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
1075#[serde(rename_all = "camelCase")]
1076pub struct HttpClassCallback {
1077 pub callback_class: String,
1078
1079 #[serde(skip_serializing_if = "Option::is_none")]
1080 pub delay: Option<Delay>,
1081
1082 #[serde(skip_serializing_if = "Option::is_none")]
1083 pub primary: Option<bool>,
1084}
1085
1086impl HttpClassCallback {
1087 pub fn new(callback_class: impl Into<String>) -> Self {
1090 Self {
1091 callback_class: callback_class.into(),
1092 delay: None,
1093 primary: None,
1094 }
1095 }
1096
1097 pub fn delay(mut self, delay: Delay) -> Self {
1099 self.delay = Some(delay);
1100 self
1101 }
1102
1103 pub fn primary(mut self, primary: bool) -> Self {
1105 self.primary = Some(primary);
1106 self
1107 }
1108}
1109
1110#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
1128#[serde(rename_all = "camelCase")]
1129pub struct HttpObjectCallback {
1130 pub client_id: String,
1131
1132 #[serde(skip_serializing_if = "Option::is_none")]
1133 pub response_callback: Option<bool>,
1134
1135 #[serde(skip_serializing_if = "Option::is_none")]
1136 pub delay: Option<Delay>,
1137
1138 #[serde(skip_serializing_if = "Option::is_none")]
1139 pub primary: Option<bool>,
1140}
1141
1142impl HttpObjectCallback {
1143 pub fn new(client_id: impl Into<String>) -> Self {
1145 Self {
1146 client_id: client_id.into(),
1147 response_callback: None,
1148 delay: None,
1149 primary: None,
1150 }
1151 }
1152
1153 pub fn response_callback(mut self, response_callback: bool) -> Self {
1155 self.response_callback = Some(response_callback);
1156 self
1157 }
1158
1159 pub fn delay(mut self, delay: Delay) -> Self {
1161 self.delay = Some(delay);
1162 self
1163 }
1164
1165 pub fn primary(mut self, primary: bool) -> Self {
1167 self.primary = Some(primary);
1168 self
1169 }
1170}
1171
1172#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
1178#[serde(rename_all = "camelCase")]
1179pub struct HttpError {
1180 #[serde(skip_serializing_if = "Option::is_none")]
1181 pub drop_connection: Option<bool>,
1182
1183 #[serde(skip_serializing_if = "Option::is_none")]
1184 pub response_bytes: Option<String>,
1185
1186 #[serde(skip_serializing_if = "Option::is_none")]
1188 pub delay: Option<Delay>,
1189}
1190
1191impl HttpError {
1192 pub fn new() -> Self {
1194 Self::default()
1195 }
1196
1197 pub fn drop_connection(mut self, drop: bool) -> Self {
1199 self.drop_connection = Some(drop);
1200 self
1201 }
1202
1203 pub fn response_bytes(mut self, bytes: impl Into<String>) -> Self {
1205 self.response_bytes = Some(bytes.into());
1206 self
1207 }
1208
1209 pub fn delay(mut self, delay: Delay) -> Self {
1211 self.delay = Some(delay);
1212 self
1213 }
1214}
1215
1216#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
1224#[serde(rename_all = "camelCase")]
1225pub struct SseEvent {
1226 #[serde(skip_serializing_if = "Option::is_none")]
1227 pub event: Option<String>,
1228
1229 #[serde(skip_serializing_if = "Option::is_none")]
1230 pub data: Option<String>,
1231
1232 #[serde(skip_serializing_if = "Option::is_none")]
1233 pub id: Option<String>,
1234
1235 #[serde(skip_serializing_if = "Option::is_none")]
1236 pub retry: Option<u32>,
1237
1238 #[serde(skip_serializing_if = "Option::is_none")]
1239 pub delay: Option<Delay>,
1240}
1241
1242impl SseEvent {
1243 pub fn new() -> Self {
1245 Self::default()
1246 }
1247
1248 pub fn event(mut self, event: impl Into<String>) -> Self {
1250 self.event = Some(event.into());
1251 self
1252 }
1253
1254 pub fn data(mut self, data: impl Into<String>) -> Self {
1256 self.data = Some(data.into());
1257 self
1258 }
1259
1260 pub fn id(mut self, id: impl Into<String>) -> Self {
1262 self.id = Some(id.into());
1263 self
1264 }
1265
1266 pub fn retry(mut self, retry: u32) -> Self {
1268 self.retry = Some(retry);
1269 self
1270 }
1271
1272 pub fn delay(mut self, delay: Delay) -> Self {
1274 self.delay = Some(delay);
1275 self
1276 }
1277}
1278
1279#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
1294#[serde(rename_all = "camelCase")]
1295pub struct HttpSseResponse {
1296 #[serde(skip_serializing_if = "Option::is_none")]
1297 pub status_code: Option<u16>,
1298
1299 #[serde(skip_serializing_if = "Option::is_none")]
1300 pub headers: Option<HashMap<String, Vec<String>>>,
1301
1302 #[serde(skip_serializing_if = "Option::is_none")]
1303 pub events: Option<Vec<SseEvent>>,
1304
1305 #[serde(skip_serializing_if = "Option::is_none")]
1306 pub close_connection: Option<bool>,
1307
1308 #[serde(skip_serializing_if = "Option::is_none")]
1309 pub delay: Option<Delay>,
1310}
1311
1312impl HttpSseResponse {
1313 pub fn new() -> Self {
1315 Self::default()
1316 }
1317
1318 pub fn status_code(mut self, code: u16) -> Self {
1320 self.status_code = Some(code);
1321 self
1322 }
1323
1324 pub fn header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
1326 let headers = self.headers.get_or_insert_with(HashMap::new);
1327 headers.entry(key.into()).or_default().push(value.into());
1328 self
1329 }
1330
1331 pub fn event(mut self, event: SseEvent) -> Self {
1333 self.events.get_or_insert_with(Vec::new).push(event);
1334 self
1335 }
1336
1337 pub fn events(mut self, events: Vec<SseEvent>) -> Self {
1339 self.events = Some(events);
1340 self
1341 }
1342
1343 pub fn close_connection(mut self, close: bool) -> Self {
1345 self.close_connection = Some(close);
1346 self
1347 }
1348
1349 pub fn delay(mut self, delay: Delay) -> Self {
1351 self.delay = Some(delay);
1352 self
1353 }
1354}
1355
1356#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
1365#[serde(rename_all = "camelCase")]
1366pub struct WebSocketMessage {
1367 #[serde(skip_serializing_if = "Option::is_none")]
1368 pub text: Option<String>,
1369
1370 #[serde(skip_serializing_if = "Option::is_none")]
1371 pub binary: Option<String>,
1372
1373 #[serde(skip_serializing_if = "Option::is_none")]
1374 pub delay: Option<Delay>,
1375}
1376
1377impl WebSocketMessage {
1378 pub fn text(text: impl Into<String>) -> Self {
1380 Self {
1381 text: Some(text.into()),
1382 binary: None,
1383 delay: None,
1384 }
1385 }
1386
1387 pub fn binary(data: impl AsRef<[u8]>) -> Self {
1389 Self {
1390 text: None,
1391 binary: Some(BASE64.encode(data.as_ref())),
1392 delay: None,
1393 }
1394 }
1395
1396 pub fn binary_base64(base64: impl Into<String>) -> Self {
1398 Self {
1399 text: None,
1400 binary: Some(base64.into()),
1401 delay: None,
1402 }
1403 }
1404
1405 pub fn delay(mut self, delay: Delay) -> Self {
1407 self.delay = Some(delay);
1408 self
1409 }
1410}
1411
1412#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
1419#[serde(rename_all = "camelCase")]
1420pub struct WebSocketMatcher {
1421 #[serde(skip_serializing_if = "Option::is_none")]
1423 pub frame_type: Option<String>,
1424
1425 #[serde(skip_serializing_if = "Option::is_none")]
1427 pub text_matcher: Option<String>,
1428
1429 #[serde(skip_serializing_if = "Option::is_none")]
1431 pub responses: Option<Vec<WebSocketMessage>>,
1432
1433 #[serde(flatten, default)]
1435 pub extra: Extra,
1436}
1437
1438impl WebSocketMatcher {
1439 pub fn new() -> Self {
1441 Self::default()
1442 }
1443
1444 pub fn frame_type(mut self, frame_type: impl Into<String>) -> Self {
1446 self.frame_type = Some(frame_type.into());
1447 self
1448 }
1449
1450 pub fn text_matcher(mut self, text_matcher: impl Into<String>) -> Self {
1452 self.text_matcher = Some(text_matcher.into());
1453 self
1454 }
1455
1456 pub fn response(mut self, response: WebSocketMessage) -> Self {
1458 self.responses.get_or_insert_with(Vec::new).push(response);
1459 self
1460 }
1461
1462 pub fn responses(mut self, responses: Vec<WebSocketMessage>) -> Self {
1464 self.responses = Some(responses);
1465 self
1466 }
1467}
1468
1469#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
1484#[serde(rename_all = "camelCase")]
1485pub struct HttpWebSocketResponse {
1486 #[serde(skip_serializing_if = "Option::is_none")]
1487 pub subprotocol: Option<String>,
1488
1489 #[serde(skip_serializing_if = "Option::is_none")]
1490 pub messages: Option<Vec<WebSocketMessage>>,
1491
1492 #[serde(skip_serializing_if = "Option::is_none")]
1495 pub matchers: Option<Vec<WebSocketMatcher>>,
1496
1497 #[serde(skip_serializing_if = "Option::is_none")]
1498 pub close_connection: Option<bool>,
1499
1500 #[serde(skip_serializing_if = "Option::is_none")]
1501 pub delay: Option<Delay>,
1502}
1503
1504impl HttpWebSocketResponse {
1505 pub fn new() -> Self {
1507 Self::default()
1508 }
1509
1510 pub fn matcher(mut self, matcher: WebSocketMatcher) -> Self {
1512 self.matchers.get_or_insert_with(Vec::new).push(matcher);
1513 self
1514 }
1515
1516 pub fn matchers(mut self, matchers: Vec<WebSocketMatcher>) -> Self {
1518 self.matchers = Some(matchers);
1519 self
1520 }
1521
1522 pub fn subprotocol(mut self, subprotocol: impl Into<String>) -> Self {
1524 self.subprotocol = Some(subprotocol.into());
1525 self
1526 }
1527
1528 pub fn message(mut self, message: WebSocketMessage) -> Self {
1530 self.messages.get_or_insert_with(Vec::new).push(message);
1531 self
1532 }
1533
1534 pub fn messages(mut self, messages: Vec<WebSocketMessage>) -> Self {
1536 self.messages = Some(messages);
1537 self
1538 }
1539
1540 pub fn close_connection(mut self, close: bool) -> Self {
1542 self.close_connection = Some(close);
1543 self
1544 }
1545
1546 pub fn delay(mut self, delay: Delay) -> Self {
1548 self.delay = Some(delay);
1549 self
1550 }
1551}
1552
1553#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
1559#[serde(rename_all = "camelCase")]
1560pub struct DnsRecord {
1561 #[serde(skip_serializing_if = "Option::is_none")]
1562 pub name: Option<String>,
1563
1564 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
1565 pub record_type: Option<String>,
1566
1567 #[serde(skip_serializing_if = "Option::is_none")]
1568 pub dns_class: Option<String>,
1569
1570 #[serde(skip_serializing_if = "Option::is_none")]
1571 pub ttl: Option<u32>,
1572
1573 #[serde(skip_serializing_if = "Option::is_none")]
1574 pub value: Option<String>,
1575
1576 #[serde(skip_serializing_if = "Option::is_none")]
1577 pub priority: Option<u32>,
1578
1579 #[serde(skip_serializing_if = "Option::is_none")]
1580 pub weight: Option<u32>,
1581
1582 #[serde(skip_serializing_if = "Option::is_none")]
1583 pub port: Option<u16>,
1584}
1585
1586impl DnsRecord {
1587 pub fn new() -> Self {
1589 Self::default()
1590 }
1591
1592 pub fn a(name: impl Into<String>, ip: impl Into<String>) -> Self {
1594 Self::new().name(name).record_type("A").value(ip)
1595 }
1596
1597 pub fn aaaa(name: impl Into<String>, ip: impl Into<String>) -> Self {
1599 Self::new().name(name).record_type("AAAA").value(ip)
1600 }
1601
1602 pub fn cname(name: impl Into<String>, target: impl Into<String>) -> Self {
1604 Self::new().name(name).record_type("CNAME").value(target)
1605 }
1606
1607 pub fn txt(name: impl Into<String>, text: impl Into<String>) -> Self {
1609 Self::new().name(name).record_type("TXT").value(text)
1610 }
1611
1612 pub fn name(mut self, name: impl Into<String>) -> Self {
1614 self.name = Some(name.into());
1615 self
1616 }
1617
1618 pub fn record_type(mut self, record_type: impl Into<String>) -> Self {
1620 self.record_type = Some(record_type.into());
1621 self
1622 }
1623
1624 pub fn dns_class(mut self, dns_class: impl Into<String>) -> Self {
1626 self.dns_class = Some(dns_class.into());
1627 self
1628 }
1629
1630 pub fn ttl(mut self, ttl: u32) -> Self {
1632 self.ttl = Some(ttl);
1633 self
1634 }
1635
1636 pub fn value(mut self, value: impl Into<String>) -> Self {
1638 self.value = Some(value.into());
1639 self
1640 }
1641
1642 pub fn priority(mut self, priority: u32) -> Self {
1644 self.priority = Some(priority);
1645 self
1646 }
1647
1648 pub fn weight(mut self, weight: u32) -> Self {
1650 self.weight = Some(weight);
1651 self
1652 }
1653
1654 pub fn port(mut self, port: u16) -> Self {
1656 self.port = Some(port);
1657 self
1658 }
1659}
1660
1661#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
1674#[serde(rename_all = "camelCase")]
1675pub struct DnsResponse {
1676 #[serde(skip_serializing_if = "Option::is_none")]
1677 pub answer_records: Option<Vec<DnsRecord>>,
1678
1679 #[serde(skip_serializing_if = "Option::is_none")]
1680 pub authority_records: Option<Vec<DnsRecord>>,
1681
1682 #[serde(skip_serializing_if = "Option::is_none")]
1683 pub additional_records: Option<Vec<DnsRecord>>,
1684
1685 #[serde(skip_serializing_if = "Option::is_none")]
1686 pub response_code: Option<String>,
1687
1688 #[serde(skip_serializing_if = "Option::is_none")]
1689 pub delay: Option<Delay>,
1690}
1691
1692impl DnsResponse {
1693 pub fn new() -> Self {
1695 Self::default()
1696 }
1697
1698 pub fn answer_record(mut self, record: DnsRecord) -> Self {
1700 self.answer_records
1701 .get_or_insert_with(Vec::new)
1702 .push(record);
1703 self
1704 }
1705
1706 pub fn answer_records(mut self, records: Vec<DnsRecord>) -> Self {
1708 self.answer_records = Some(records);
1709 self
1710 }
1711
1712 pub fn authority_record(mut self, record: DnsRecord) -> Self {
1714 self.authority_records
1715 .get_or_insert_with(Vec::new)
1716 .push(record);
1717 self
1718 }
1719
1720 pub fn additional_record(mut self, record: DnsRecord) -> Self {
1722 self.additional_records
1723 .get_or_insert_with(Vec::new)
1724 .push(record);
1725 self
1726 }
1727
1728 pub fn response_code(mut self, code: impl Into<String>) -> Self {
1730 self.response_code = Some(code.into());
1731 self
1732 }
1733
1734 pub fn delay(mut self, delay: Delay) -> Self {
1736 self.delay = Some(delay);
1737 self
1738 }
1739}
1740
1741#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
1758#[serde(rename_all = "camelCase")]
1759pub struct BinaryResponse {
1760 #[serde(skip_serializing_if = "Option::is_none")]
1761 pub binary_data: Option<String>,
1762
1763 #[serde(skip_serializing_if = "Option::is_none")]
1764 pub delay: Option<Delay>,
1765}
1766
1767impl BinaryResponse {
1768 pub fn new() -> Self {
1770 Self::default()
1771 }
1772
1773 pub fn from_bytes(data: impl AsRef<[u8]>) -> Self {
1775 Self {
1776 binary_data: Some(BASE64.encode(data.as_ref())),
1777 delay: None,
1778 }
1779 }
1780
1781 pub fn from_base64(base64: impl Into<String>) -> Self {
1783 Self {
1784 binary_data: Some(base64.into()),
1785 delay: None,
1786 }
1787 }
1788
1789 pub fn binary_data(mut self, data: impl AsRef<[u8]>) -> Self {
1791 self.binary_data = Some(BASE64.encode(data.as_ref()));
1792 self
1793 }
1794
1795 pub fn delay(mut self, delay: Delay) -> Self {
1797 self.delay = Some(delay);
1798 self
1799 }
1800}
1801
1802#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
1808#[serde(rename_all = "camelCase")]
1809pub struct GrpcStreamMessage {
1810 #[serde(skip_serializing_if = "Option::is_none")]
1811 pub json: Option<String>,
1812
1813 #[serde(skip_serializing_if = "Option::is_none")]
1816 pub template_type: Option<String>,
1817
1818 #[serde(skip_serializing_if = "Option::is_none")]
1819 pub delay: Option<Delay>,
1820}
1821
1822impl GrpcStreamMessage {
1823 pub fn json(json: impl Into<String>) -> Self {
1825 Self {
1826 json: Some(json.into()),
1827 template_type: None,
1828 delay: None,
1829 }
1830 }
1831
1832 pub fn template_type(mut self, template_type: impl Into<String>) -> Self {
1834 self.template_type = Some(template_type.into());
1835 self
1836 }
1837
1838 pub fn delay(mut self, delay: Delay) -> Self {
1840 self.delay = Some(delay);
1841 self
1842 }
1843}
1844
1845#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
1859#[serde(rename_all = "camelCase")]
1860pub struct GrpcStreamResponse {
1861 #[serde(skip_serializing_if = "Option::is_none")]
1862 pub status_name: Option<String>,
1863
1864 #[serde(skip_serializing_if = "Option::is_none")]
1865 pub status_message: Option<String>,
1866
1867 #[serde(skip_serializing_if = "Option::is_none")]
1868 pub headers: Option<HashMap<String, Vec<String>>>,
1869
1870 #[serde(skip_serializing_if = "Option::is_none")]
1871 pub messages: Option<Vec<GrpcStreamMessage>>,
1872
1873 #[serde(skip_serializing_if = "Option::is_none")]
1874 pub close_connection: Option<bool>,
1875
1876 #[serde(skip_serializing_if = "Option::is_none")]
1877 pub delay: Option<Delay>,
1878}
1879
1880impl GrpcStreamResponse {
1881 pub fn new() -> Self {
1883 Self::default()
1884 }
1885
1886 pub fn status_name(mut self, status_name: impl Into<String>) -> Self {
1888 self.status_name = Some(status_name.into());
1889 self
1890 }
1891
1892 pub fn status_message(mut self, status_message: impl Into<String>) -> Self {
1894 self.status_message = Some(status_message.into());
1895 self
1896 }
1897
1898 pub fn header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
1900 let headers = self.headers.get_or_insert_with(HashMap::new);
1901 headers.entry(key.into()).or_default().push(value.into());
1902 self
1903 }
1904
1905 pub fn message(mut self, message: GrpcStreamMessage) -> Self {
1907 self.messages.get_or_insert_with(Vec::new).push(message);
1908 self
1909 }
1910
1911 pub fn messages(mut self, messages: Vec<GrpcStreamMessage>) -> Self {
1913 self.messages = Some(messages);
1914 self
1915 }
1916
1917 pub fn close_connection(mut self, close: bool) -> Self {
1919 self.close_connection = Some(close);
1920 self
1921 }
1922
1923 pub fn delay(mut self, delay: Delay) -> Self {
1925 self.delay = Some(delay);
1926 self
1927 }
1928}
1929
1930#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
1951#[serde(rename_all = "camelCase")]
1952pub struct OpenApiExpectation {
1953 pub spec_url_or_payload: String,
1954
1955 #[serde(skip_serializing_if = "Option::is_none")]
1956 pub operations_and_responses: Option<HashMap<String, String>>,
1957
1958 #[serde(skip_serializing_if = "Option::is_none")]
1959 pub context_path_prefix: Option<String>,
1960}
1961
1962impl OpenApiExpectation {
1963 pub fn new(spec_url_or_payload: impl Into<String>) -> Self {
1966 Self {
1967 spec_url_or_payload: spec_url_or_payload.into(),
1968 operations_and_responses: None,
1969 context_path_prefix: None,
1970 }
1971 }
1972
1973 pub fn operation(
1978 mut self,
1979 operation_id: impl Into<String>,
1980 status_code: impl Into<String>,
1981 ) -> Self {
1982 self.operations_and_responses
1983 .get_or_insert_with(HashMap::new)
1984 .insert(operation_id.into(), status_code.into());
1985 self
1986 }
1987
1988 pub fn operations_and_responses(mut self, map: HashMap<String, String>) -> Self {
1990 self.operations_and_responses = Some(map);
1991 self
1992 }
1993
1994 pub fn context_path_prefix(mut self, prefix: impl Into<String>) -> Self {
1996 self.context_path_prefix = Some(prefix.into());
1997 self
1998 }
1999}
2000
2001#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2007#[serde(rename_all = "camelCase")]
2008pub struct Delay {
2009 pub time_unit: String,
2010 pub value: u64,
2011}
2012
2013impl Delay {
2014 pub fn milliseconds(value: u64) -> Self {
2016 Self {
2017 time_unit: "MILLISECONDS".to_string(),
2018 value,
2019 }
2020 }
2021
2022 pub fn seconds(value: u64) -> Self {
2024 Self {
2025 time_unit: "SECONDS".to_string(),
2026 value,
2027 }
2028 }
2029}
2030
2031#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2037#[serde(rename_all = "camelCase")]
2038pub struct Times {
2039 #[serde(skip_serializing_if = "Option::is_none")]
2040 pub remaining_times: Option<u32>,
2041
2042 #[serde(default)]
2043 pub unlimited: bool,
2044}
2045
2046impl Times {
2047 pub fn unlimited() -> Self {
2049 Self {
2050 remaining_times: None,
2051 unlimited: true,
2052 }
2053 }
2054
2055 pub fn exactly(n: u32) -> Self {
2057 Self {
2058 remaining_times: Some(n),
2059 unlimited: false,
2060 }
2061 }
2062
2063 pub fn once() -> Self {
2065 Self::exactly(1)
2066 }
2067}
2068
2069#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2075#[serde(rename_all = "camelCase")]
2076pub struct TimeToLive {
2077 #[serde(skip_serializing_if = "Option::is_none")]
2078 pub time_unit: Option<String>,
2079
2080 #[serde(skip_serializing_if = "Option::is_none")]
2081 pub time_to_live: Option<u64>,
2082
2083 #[serde(default)]
2084 pub unlimited: bool,
2085}
2086
2087impl TimeToLive {
2088 pub fn unlimited() -> Self {
2090 Self {
2091 time_unit: None,
2092 time_to_live: None,
2093 unlimited: true,
2094 }
2095 }
2096
2097 pub fn seconds(seconds: u64) -> Self {
2099 Self {
2100 time_unit: Some("SECONDS".to_string()),
2101 time_to_live: Some(seconds),
2102 unlimited: false,
2103 }
2104 }
2105
2106 pub fn milliseconds(millis: u64) -> Self {
2108 Self {
2109 time_unit: Some("MILLISECONDS".to_string()),
2110 time_to_live: Some(millis),
2111 unlimited: false,
2112 }
2113 }
2114}
2115
2116#[derive(Debug, Clone, Deserialize, PartialEq)]
2128#[serde(rename_all = "camelCase")]
2129pub struct VerificationTimes {
2130 pub at_least: Option<u32>,
2131 pub at_most: Option<u32>,
2132}
2133
2134impl Serialize for VerificationTimes {
2135 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
2136 where
2137 S: serde::Serializer,
2138 {
2139 use serde::ser::SerializeStruct;
2140 let mut state = serializer.serialize_struct("VerificationTimes", 2)?;
2141 state.serialize_field("atLeast", &self.at_least.map_or(-1_i64, i64::from))?;
2142 state.serialize_field("atMost", &self.at_most.map_or(-1_i64, i64::from))?;
2143 state.end()
2144 }
2145}
2146
2147impl VerificationTimes {
2148 pub fn at_least(n: u32) -> Self {
2150 Self {
2151 at_least: Some(n),
2152 at_most: None,
2153 }
2154 }
2155
2156 pub fn at_most(n: u32) -> Self {
2158 Self {
2159 at_least: None,
2160 at_most: Some(n),
2161 }
2162 }
2163
2164 pub fn exactly(n: u32) -> Self {
2166 Self {
2167 at_least: Some(n),
2168 at_most: Some(n),
2169 }
2170 }
2171
2172 pub fn between(min: u32, max: u32) -> Self {
2174 Self {
2175 at_least: Some(min),
2176 at_most: Some(max),
2177 }
2178 }
2179}
2180
2181#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
2195#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
2196pub enum ResponseMode {
2197 Sequential,
2199 Random,
2201 Weighted,
2203 Switch,
2205}
2206
2207#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
2210#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
2211pub enum CrossProtocolTrigger {
2212 DnsQuery,
2214 WebsocketConnect,
2216 GrpcRequest,
2218 HttpRequest,
2220}
2221
2222#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2240#[serde(rename_all = "camelCase")]
2241pub struct CrossProtocolScenario {
2242 pub trigger: CrossProtocolTrigger,
2243
2244 #[serde(skip_serializing_if = "Option::is_none")]
2245 pub match_pattern: Option<String>,
2246
2247 pub scenario_name: String,
2248
2249 pub target_state: String,
2250}
2251
2252impl CrossProtocolScenario {
2253 pub fn new(
2256 trigger: CrossProtocolTrigger,
2257 scenario_name: impl Into<String>,
2258 target_state: impl Into<String>,
2259 ) -> Self {
2260 Self {
2261 trigger,
2262 match_pattern: None,
2263 scenario_name: scenario_name.into(),
2264 target_state: target_state.into(),
2265 }
2266 }
2267
2268 pub fn match_pattern(mut self, pattern: impl Into<String>) -> Self {
2270 self.match_pattern = Some(pattern.into());
2271 self
2272 }
2273}
2274
2275#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
2282#[serde(rename_all = "camelCase")]
2283pub struct ConnectionOptions {
2284 #[serde(skip_serializing_if = "Option::is_none")]
2285 pub suppress_content_length_header: Option<bool>,
2286
2287 #[serde(skip_serializing_if = "Option::is_none")]
2288 pub content_length_header_override: Option<i64>,
2289
2290 #[serde(skip_serializing_if = "Option::is_none")]
2291 pub suppress_connection_header: Option<bool>,
2292
2293 #[serde(skip_serializing_if = "Option::is_none")]
2294 pub chunk_size: Option<i64>,
2295
2296 #[serde(skip_serializing_if = "Option::is_none")]
2297 pub chunk_delay: Option<Delay>,
2298
2299 #[serde(skip_serializing_if = "Option::is_none")]
2300 pub keep_alive_override: Option<bool>,
2301
2302 #[serde(skip_serializing_if = "Option::is_none")]
2303 pub close_socket: Option<bool>,
2304
2305 #[serde(skip_serializing_if = "Option::is_none")]
2306 pub close_socket_delay: Option<Delay>,
2307
2308 #[serde(flatten, default)]
2309 pub extra: Extra,
2310}
2311
2312impl ConnectionOptions {
2313 pub fn new() -> Self {
2315 Self::default()
2316 }
2317}
2318
2319#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
2322#[serde(rename_all = "camelCase")]
2323pub struct RecoverAfter {
2324 #[serde(skip_serializing_if = "Option::is_none")]
2325 pub fail_times: Option<i64>,
2326
2327 #[serde(skip_serializing_if = "Option::is_none")]
2328 pub fail_response: Option<serde_json::Value>,
2329
2330 #[serde(skip_serializing_if = "Option::is_none")]
2331 pub idempotency_header: Option<String>,
2332
2333 #[serde(flatten, default)]
2334 pub extra: Extra,
2335}
2336
2337impl RecoverAfter {
2338 pub fn new() -> Self {
2340 Self::default()
2341 }
2342}
2343
2344#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
2350#[serde(rename_all = "camelCase")]
2351pub struct RateLimit {
2352 #[serde(skip_serializing_if = "Option::is_none")]
2353 pub name: Option<String>,
2354
2355 #[serde(skip_serializing_if = "Option::is_none")]
2357 pub algorithm: Option<String>,
2358
2359 #[serde(skip_serializing_if = "Option::is_none")]
2360 pub limit: Option<i64>,
2361
2362 #[serde(skip_serializing_if = "Option::is_none")]
2363 pub window_millis: Option<i64>,
2364
2365 #[serde(skip_serializing_if = "Option::is_none")]
2366 pub burst: Option<i64>,
2367
2368 #[serde(skip_serializing_if = "Option::is_none")]
2369 pub refill_per_second: Option<f64>,
2370
2371 #[serde(skip_serializing_if = "Option::is_none")]
2372 pub error_status: Option<i32>,
2373
2374 #[serde(skip_serializing_if = "Option::is_none")]
2375 pub retry_after: Option<String>,
2376
2377 #[serde(flatten, default)]
2378 pub extra: Extra,
2379}
2380
2381impl RateLimit {
2382 pub fn new() -> Self {
2384 Self::default()
2385 }
2386
2387 pub fn fixed_window(limit: i64, window_millis: i64) -> Self {
2389 Self {
2390 algorithm: Some("fixed_window".to_string()),
2391 limit: Some(limit),
2392 window_millis: Some(window_millis),
2393 ..Default::default()
2394 }
2395 }
2396
2397 pub fn token_bucket(burst: i64, refill_per_second: f64) -> Self {
2400 Self {
2401 algorithm: Some("token_bucket".to_string()),
2402 burst: Some(burst),
2403 refill_per_second: Some(refill_per_second),
2404 ..Default::default()
2405 }
2406 }
2407}
2408
2409#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2416#[serde(rename_all = "camelCase")]
2417pub struct HttpForwardWithFallback {
2418 pub http_forward: HttpForward,
2419
2420 pub fallback_response: HttpResponse,
2421
2422 #[serde(skip_serializing_if = "Option::is_none")]
2423 pub fallback_on_status_codes: Option<Vec<i32>>,
2424
2425 #[serde(skip_serializing_if = "Option::is_none")]
2426 pub fallback_on_timeout: Option<bool>,
2427
2428 #[serde(skip_serializing_if = "Option::is_none")]
2429 pub delay: Option<Delay>,
2430
2431 #[serde(skip_serializing_if = "Option::is_none")]
2432 pub primary: Option<bool>,
2433
2434 #[serde(flatten, default)]
2435 pub extra: Extra,
2436}
2437
2438impl HttpForwardWithFallback {
2439 pub fn new(http_forward: HttpForward, fallback_response: HttpResponse) -> Self {
2441 Self {
2442 http_forward,
2443 fallback_response,
2444 fallback_on_status_codes: None,
2445 fallback_on_timeout: None,
2446 delay: None,
2447 primary: None,
2448 extra: Extra::new(),
2449 }
2450 }
2451
2452 pub fn fallback_on_status_codes(mut self, codes: Vec<i32>) -> Self {
2454 self.fallback_on_status_codes = Some(codes);
2455 self
2456 }
2457
2458 pub fn fallback_on_timeout(mut self, fallback: bool) -> Self {
2460 self.fallback_on_timeout = Some(fallback);
2461 self
2462 }
2463}
2464
2465#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2468#[serde(rename_all = "camelCase")]
2469pub struct HttpForwardValidateAction {
2470 pub spec_url_or_payload: String,
2471
2472 pub host: String,
2473
2474 #[serde(skip_serializing_if = "Option::is_none")]
2475 pub port: Option<u16>,
2476
2477 #[serde(skip_serializing_if = "Option::is_none")]
2478 pub scheme: Option<String>,
2479
2480 #[serde(skip_serializing_if = "Option::is_none")]
2481 pub validate_request: Option<bool>,
2482
2483 #[serde(skip_serializing_if = "Option::is_none")]
2484 pub validate_response: Option<bool>,
2485
2486 #[serde(skip_serializing_if = "Option::is_none")]
2488 pub validation_mode: Option<String>,
2489
2490 #[serde(skip_serializing_if = "Option::is_none")]
2491 pub delay: Option<Delay>,
2492
2493 #[serde(skip_serializing_if = "Option::is_none")]
2494 pub primary: Option<bool>,
2495
2496 #[serde(flatten, default)]
2497 pub extra: Extra,
2498}
2499
2500impl HttpForwardValidateAction {
2501 pub fn new(spec_url_or_payload: impl Into<String>, host: impl Into<String>) -> Self {
2503 Self {
2504 spec_url_or_payload: spec_url_or_payload.into(),
2505 host: host.into(),
2506 port: None,
2507 scheme: None,
2508 validate_request: None,
2509 validate_response: None,
2510 validation_mode: None,
2511 delay: None,
2512 primary: None,
2513 extra: Extra::new(),
2514 }
2515 }
2516}
2517
2518#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
2528#[serde(rename_all = "camelCase")]
2529pub struct HttpOverrideForwardedRequest {
2530 #[serde(skip_serializing_if = "Option::is_none")]
2531 pub delay: Option<Delay>,
2532
2533 #[serde(skip_serializing_if = "Option::is_none")]
2534 pub request_override: Option<HttpRequest>,
2535
2536 #[serde(skip_serializing_if = "Option::is_none")]
2537 pub request_modifier: Option<serde_json::Value>,
2538
2539 #[serde(skip_serializing_if = "Option::is_none")]
2540 pub response_override: Option<HttpResponse>,
2541
2542 #[serde(skip_serializing_if = "Option::is_none")]
2543 pub response_modifier: Option<serde_json::Value>,
2544
2545 #[serde(skip_serializing_if = "Option::is_none")]
2546 pub response_template: Option<HttpTemplate>,
2547
2548 #[serde(skip_serializing_if = "Option::is_none")]
2550 pub http_request: Option<HttpRequest>,
2551
2552 #[serde(skip_serializing_if = "Option::is_none")]
2554 pub http_response: Option<HttpResponse>,
2555
2556 #[serde(skip_serializing_if = "Option::is_none")]
2557 pub primary: Option<bool>,
2558
2559 #[serde(flatten, default)]
2560 pub extra: Extra,
2561}
2562
2563impl HttpOverrideForwardedRequest {
2564 pub fn new() -> Self {
2566 Self::default()
2567 }
2568
2569 pub fn request_override(mut self, request: HttpRequest) -> Self {
2571 self.request_override = Some(request);
2572 self
2573 }
2574
2575 pub fn response_override(mut self, response: HttpResponse) -> Self {
2577 self.response_override = Some(response);
2578 self
2579 }
2580}
2581
2582#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
2590#[serde(rename_all = "camelCase")]
2591pub struct ExpectationAction {
2592 #[serde(skip_serializing_if = "Option::is_none")]
2593 pub http_request: Option<HttpRequest>,
2594
2595 #[serde(skip_serializing_if = "Option::is_none")]
2596 pub http_class_callback: Option<HttpClassCallback>,
2597
2598 #[serde(skip_serializing_if = "Option::is_none")]
2599 pub http_object_callback: Option<HttpObjectCallback>,
2600
2601 #[serde(skip_serializing_if = "Option::is_none")]
2602 pub delay: Option<Delay>,
2603
2604 #[serde(skip_serializing_if = "Option::is_none")]
2605 pub blocking: Option<bool>,
2606
2607 #[serde(skip_serializing_if = "Option::is_none")]
2608 pub timeout: Option<Delay>,
2609
2610 #[serde(skip_serializing_if = "Option::is_none")]
2612 pub failure_policy: Option<String>,
2613
2614 #[serde(flatten, default)]
2615 pub extra: Extra,
2616}
2617
2618impl ExpectationAction {
2619 pub fn request(request: HttpRequest) -> Self {
2621 Self {
2622 http_request: Some(request),
2623 ..Default::default()
2624 }
2625 }
2626
2627 pub fn class_callback(callback: HttpClassCallback) -> Self {
2629 Self {
2630 http_class_callback: Some(callback),
2631 ..Default::default()
2632 }
2633 }
2634}
2635
2636#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2639#[serde(rename_all = "camelCase")]
2640pub struct CaptureRule {
2641 pub source: String,
2644
2645 pub expression: String,
2646
2647 pub into: String,
2648
2649 #[serde(flatten, default)]
2650 pub extra: Extra,
2651}
2652
2653impl CaptureRule {
2654 pub fn new(
2657 source: impl Into<String>,
2658 expression: impl Into<String>,
2659 into: impl Into<String>,
2660 ) -> Self {
2661 Self {
2662 source: source.into(),
2663 expression: expression.into(),
2664 into: into.into(),
2665 extra: Extra::new(),
2666 }
2667 }
2668}
2669
2670#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
2673#[serde(rename_all = "camelCase")]
2674pub struct ExpectationStep {
2675 #[serde(skip_serializing_if = "Option::is_none")]
2676 pub http_request: Option<HttpRequest>,
2677
2678 #[serde(skip_serializing_if = "Option::is_none")]
2679 pub http_class_callback: Option<HttpClassCallback>,
2680
2681 #[serde(skip_serializing_if = "Option::is_none")]
2682 pub http_object_callback: Option<HttpObjectCallback>,
2683
2684 #[serde(skip_serializing_if = "Option::is_none")]
2685 pub http_forward: Option<HttpForward>,
2686
2687 #[serde(skip_serializing_if = "Option::is_none")]
2688 pub http_override_forwarded_request: Option<HttpOverrideForwardedRequest>,
2689
2690 #[serde(skip_serializing_if = "Option::is_none")]
2691 pub http_response: Option<HttpResponse>,
2692
2693 #[serde(skip_serializing_if = "Option::is_none")]
2694 pub http_error: Option<HttpError>,
2695
2696 #[serde(skip_serializing_if = "Option::is_none")]
2697 pub responder: Option<bool>,
2698
2699 #[serde(skip_serializing_if = "Option::is_none")]
2700 pub delay: Option<Delay>,
2701
2702 #[serde(skip_serializing_if = "Option::is_none")]
2703 pub blocking: Option<bool>,
2704
2705 #[serde(skip_serializing_if = "Option::is_none")]
2706 pub timeout: Option<Delay>,
2707
2708 #[serde(skip_serializing_if = "Option::is_none")]
2710 pub failure_policy: Option<String>,
2711
2712 #[serde(flatten, default)]
2713 pub extra: Extra,
2714}
2715
2716impl ExpectationStep {
2717 pub fn new() -> Self {
2719 Self::default()
2720 }
2721
2722 pub fn response(response: HttpResponse) -> Self {
2724 Self {
2725 http_response: Some(response),
2726 responder: Some(true),
2727 ..Default::default()
2728 }
2729 }
2730}
2731
2732#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
2738#[serde(rename_all = "camelCase")]
2739pub struct GrpcBidiMessage {
2740 #[serde(skip_serializing_if = "Option::is_none")]
2741 pub json: Option<String>,
2742
2743 #[serde(skip_serializing_if = "Option::is_none")]
2745 pub template_type: Option<String>,
2746
2747 #[serde(skip_serializing_if = "Option::is_none")]
2748 pub delay: Option<Delay>,
2749
2750 #[serde(flatten, default)]
2751 pub extra: Extra,
2752}
2753
2754impl GrpcBidiMessage {
2755 pub fn json(json: impl Into<String>) -> Self {
2757 Self {
2758 json: Some(json.into()),
2759 ..Default::default()
2760 }
2761 }
2762}
2763
2764#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
2767#[serde(rename_all = "camelCase")]
2768pub struct GrpcBidiRule {
2769 #[serde(skip_serializing_if = "Option::is_none")]
2770 pub match_json: Option<String>,
2771
2772 #[serde(skip_serializing_if = "Option::is_none")]
2773 pub responses: Option<Vec<GrpcBidiMessage>>,
2774
2775 #[serde(flatten, default)]
2776 pub extra: Extra,
2777}
2778
2779#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
2781#[serde(rename_all = "camelCase")]
2782pub struct GrpcBidiResponse {
2783 #[serde(skip_serializing_if = "Option::is_none")]
2784 pub status_name: Option<String>,
2785
2786 #[serde(skip_serializing_if = "Option::is_none")]
2787 pub status_message: Option<String>,
2788
2789 #[serde(skip_serializing_if = "Option::is_none")]
2790 pub headers: Option<HashMap<String, Vec<String>>>,
2791
2792 #[serde(skip_serializing_if = "Option::is_none")]
2793 pub messages: Option<Vec<GrpcBidiMessage>>,
2794
2795 #[serde(skip_serializing_if = "Option::is_none")]
2796 pub rules: Option<Vec<GrpcBidiRule>>,
2797
2798 #[serde(skip_serializing_if = "Option::is_none")]
2799 pub close_connection: Option<bool>,
2800
2801 #[serde(skip_serializing_if = "Option::is_none")]
2802 pub delay: Option<Delay>,
2803
2804 #[serde(skip_serializing_if = "Option::is_none")]
2805 pub primary: Option<bool>,
2806
2807 #[serde(flatten, default)]
2808 pub extra: Extra,
2809}
2810
2811impl GrpcBidiResponse {
2812 pub fn new() -> Self {
2814 Self::default()
2815 }
2816
2817 pub fn message(mut self, message: GrpcBidiMessage) -> Self {
2819 self.messages.get_or_insert_with(Vec::new).push(message);
2820 self
2821 }
2822
2823 pub fn rule(mut self, rule: GrpcBidiRule) -> Self {
2825 self.rules.get_or_insert_with(Vec::new).push(rule);
2826 self
2827 }
2828}
2829
2830#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
2836#[serde(rename_all = "camelCase")]
2837pub struct LlmToolCall {
2838 #[serde(skip_serializing_if = "Option::is_none")]
2839 pub id: Option<String>,
2840
2841 #[serde(skip_serializing_if = "Option::is_none")]
2842 pub name: Option<String>,
2843
2844 #[serde(skip_serializing_if = "Option::is_none")]
2845 pub arguments: Option<String>,
2846
2847 #[serde(flatten, default)]
2848 pub extra: Extra,
2849}
2850
2851#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
2853#[serde(rename_all = "camelCase")]
2854pub struct LlmUsage {
2855 #[serde(skip_serializing_if = "Option::is_none")]
2856 pub input_tokens: Option<i64>,
2857
2858 #[serde(skip_serializing_if = "Option::is_none")]
2859 pub output_tokens: Option<i64>,
2860
2861 #[serde(skip_serializing_if = "Option::is_none")]
2862 pub cached_input_tokens: Option<i64>,
2863
2864 #[serde(skip_serializing_if = "Option::is_none")]
2865 pub cache_creation_tokens: Option<i64>,
2866
2867 #[serde(skip_serializing_if = "Option::is_none")]
2868 pub reasoning_tokens: Option<i64>,
2869
2870 #[serde(flatten, default)]
2871 pub extra: Extra,
2872}
2873
2874#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
2876#[serde(rename_all = "camelCase")]
2877pub struct LlmStreamingPhysics {
2878 #[serde(skip_serializing_if = "Option::is_none")]
2879 pub time_to_first_token: Option<Delay>,
2880
2881 #[serde(skip_serializing_if = "Option::is_none")]
2882 pub tokens_per_second: Option<i32>,
2883
2884 #[serde(skip_serializing_if = "Option::is_none")]
2885 pub jitter: Option<f64>,
2886
2887 #[serde(skip_serializing_if = "Option::is_none")]
2888 pub seed: Option<i64>,
2889
2890 #[serde(skip_serializing_if = "Option::is_none")]
2891 pub subword_streaming: Option<bool>,
2892
2893 #[serde(flatten, default)]
2894 pub extra: Extra,
2895}
2896
2897#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
2899#[serde(rename_all = "camelCase")]
2900pub struct LlmCompletion {
2901 #[serde(skip_serializing_if = "Option::is_none")]
2902 pub text: Option<String>,
2903
2904 #[serde(skip_serializing_if = "Option::is_none")]
2905 pub tool_calls: Option<Vec<LlmToolCall>>,
2906
2907 #[serde(skip_serializing_if = "Option::is_none")]
2908 pub stop_reason: Option<String>,
2909
2910 #[serde(skip_serializing_if = "Option::is_none")]
2911 pub usage: Option<LlmUsage>,
2912
2913 #[serde(skip_serializing_if = "Option::is_none")]
2914 pub streaming: Option<bool>,
2915
2916 #[serde(skip_serializing_if = "Option::is_none")]
2917 pub output_schema: Option<String>,
2918
2919 #[serde(skip_serializing_if = "Option::is_none")]
2920 pub enforce_output_schema: Option<bool>,
2921
2922 #[serde(skip_serializing_if = "Option::is_none")]
2923 pub tool_choice: Option<String>,
2924
2925 #[serde(skip_serializing_if = "Option::is_none")]
2926 pub reasoning_text: Option<String>,
2927
2928 #[serde(skip_serializing_if = "Option::is_none")]
2929 pub reasoning_signature: Option<String>,
2930
2931 #[serde(skip_serializing_if = "Option::is_none")]
2932 pub model: Option<String>,
2933
2934 #[serde(skip_serializing_if = "Option::is_none")]
2935 pub streaming_physics: Option<LlmStreamingPhysics>,
2936
2937 #[serde(flatten, default)]
2938 pub extra: Extra,
2939}
2940
2941impl LlmCompletion {
2942 pub fn text(text: impl Into<String>) -> Self {
2944 Self {
2945 text: Some(text.into()),
2946 ..Default::default()
2947 }
2948 }
2949}
2950
2951#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
2956#[serde(rename_all = "camelCase")]
2957pub struct HttpLlmResponse {
2958 #[serde(skip_serializing_if = "Option::is_none")]
2959 pub delay: Option<Delay>,
2960
2961 #[serde(skip_serializing_if = "Option::is_none")]
2964 pub provider: Option<String>,
2965
2966 #[serde(skip_serializing_if = "Option::is_none")]
2967 pub model: Option<String>,
2968
2969 #[serde(skip_serializing_if = "Option::is_none")]
2970 pub completion: Option<LlmCompletion>,
2971
2972 #[serde(skip_serializing_if = "Option::is_none")]
2974 pub embedding: Option<serde_json::Value>,
2975
2976 #[serde(skip_serializing_if = "Option::is_none")]
2978 pub rerank: Option<serde_json::Value>,
2979
2980 #[serde(skip_serializing_if = "Option::is_none")]
2982 pub moderation: Option<serde_json::Value>,
2983
2984 #[serde(skip_serializing_if = "Option::is_none")]
2986 pub content_filter: Option<serde_json::Value>,
2987
2988 #[serde(skip_serializing_if = "Option::is_none")]
2990 pub conversation_predicates: Option<serde_json::Value>,
2991
2992 #[serde(skip_serializing_if = "Option::is_none")]
2994 pub chaos: Option<serde_json::Value>,
2995
2996 #[serde(skip_serializing_if = "Option::is_none")]
2997 pub primary: Option<bool>,
2998
2999 #[serde(flatten, default)]
3000 pub extra: Extra,
3001}
3002
3003impl HttpLlmResponse {
3004 pub fn new() -> Self {
3006 Self::default()
3007 }
3008
3009 pub fn provider(mut self, provider: impl Into<String>) -> Self {
3011 self.provider = Some(provider.into());
3012 self
3013 }
3014
3015 pub fn model(mut self, model: impl Into<String>) -> Self {
3017 self.model = Some(model.into());
3018 self
3019 }
3020
3021 pub fn completion(mut self, completion: LlmCompletion) -> Self {
3023 self.completion = Some(completion);
3024 self
3025 }
3026}
3027
3028#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
3034#[serde(rename_all = "camelCase")]
3035pub struct Expectation {
3036 #[serde(skip_serializing_if = "Option::is_none")]
3037 pub id: Option<String>,
3038
3039 #[serde(skip_serializing_if = "Option::is_none")]
3040 pub priority: Option<i32>,
3041
3042 #[serde(skip_serializing_if = "Option::is_none")]
3044 pub percentage: Option<i32>,
3045
3046 #[serde(skip_serializing_if = "Option::is_none")]
3048 pub chaos: Option<HttpChaosProfile>,
3049
3050 #[serde(skip_serializing_if = "Option::is_none")]
3052 pub rate_limit: Option<RateLimit>,
3053
3054 #[serde(default, skip_serializing_if = "Option::is_none")]
3058 pub http_request: Option<HttpRequest>,
3059
3060 #[serde(skip_serializing_if = "Option::is_none")]
3061 pub http_response: Option<HttpResponse>,
3062
3063 #[serde(skip_serializing_if = "Option::is_none")]
3064 pub http_forward: Option<HttpForward>,
3065
3066 #[serde(skip_serializing_if = "Option::is_none")]
3067 pub http_response_template: Option<HttpTemplate>,
3068
3069 #[serde(skip_serializing_if = "Option::is_none")]
3070 pub http_forward_template: Option<HttpTemplate>,
3071
3072 #[serde(skip_serializing_if = "Option::is_none")]
3073 pub http_error: Option<HttpError>,
3074
3075 #[serde(skip_serializing_if = "Option::is_none")]
3077 pub http_response_class_callback: Option<HttpClassCallback>,
3078
3079 #[serde(skip_serializing_if = "Option::is_none")]
3081 pub http_forward_class_callback: Option<HttpClassCallback>,
3082
3083 #[serde(skip_serializing_if = "Option::is_none")]
3085 pub http_response_object_callback: Option<HttpObjectCallback>,
3086
3087 #[serde(skip_serializing_if = "Option::is_none")]
3089 pub http_forward_object_callback: Option<HttpObjectCallback>,
3090
3091 #[serde(skip_serializing_if = "Option::is_none")]
3093 pub http_override_forwarded_request: Option<HttpOverrideForwardedRequest>,
3094
3095 #[serde(skip_serializing_if = "Option::is_none")]
3097 pub http_forward_validate_action: Option<HttpForwardValidateAction>,
3098
3099 #[serde(skip_serializing_if = "Option::is_none")]
3101 pub http_forward_with_fallback: Option<HttpForwardWithFallback>,
3102
3103 #[serde(skip_serializing_if = "Option::is_none")]
3104 pub http_sse_response: Option<HttpSseResponse>,
3105
3106 #[serde(skip_serializing_if = "Option::is_none")]
3108 pub http_llm_response: Option<HttpLlmResponse>,
3109
3110 #[serde(skip_serializing_if = "Option::is_none")]
3111 pub http_web_socket_response: Option<HttpWebSocketResponse>,
3112
3113 #[serde(skip_serializing_if = "Option::is_none")]
3114 pub dns_response: Option<DnsResponse>,
3115
3116 #[serde(skip_serializing_if = "Option::is_none")]
3117 pub binary_response: Option<BinaryResponse>,
3118
3119 #[serde(skip_serializing_if = "Option::is_none")]
3120 pub grpc_stream_response: Option<GrpcStreamResponse>,
3121
3122 #[serde(skip_serializing_if = "Option::is_none")]
3124 pub grpc_bidi_response: Option<GrpcBidiResponse>,
3125
3126 #[serde(skip_serializing_if = "Option::is_none")]
3127 pub times: Option<Times>,
3128
3129 #[serde(skip_serializing_if = "Option::is_none")]
3130 pub time_to_live: Option<TimeToLive>,
3131
3132 #[serde(skip_serializing_if = "Option::is_none")]
3134 pub scenario_name: Option<String>,
3135
3136 #[serde(skip_serializing_if = "Option::is_none")]
3138 pub scenario_state: Option<String>,
3139
3140 #[serde(skip_serializing_if = "Option::is_none")]
3142 pub new_scenario_state: Option<String>,
3143
3144 #[serde(skip_serializing_if = "Option::is_none")]
3146 pub http_responses: Option<Vec<HttpResponse>>,
3147
3148 #[serde(skip_serializing_if = "Option::is_none")]
3150 pub response_mode: Option<ResponseMode>,
3151
3152 #[serde(skip_serializing_if = "Option::is_none")]
3154 pub response_weights: Option<Vec<i32>>,
3155
3156 #[serde(skip_serializing_if = "Option::is_none")]
3158 pub switch_after: Option<i32>,
3159
3160 #[serde(skip_serializing_if = "Option::is_none")]
3162 pub cross_protocol_scenarios: Option<Vec<CrossProtocolScenario>>,
3163
3164 #[serde(
3169 skip_serializing_if = "Option::is_none",
3170 deserialize_with = "one_or_many",
3171 default
3172 )]
3173 pub before_actions: Option<Vec<ExpectationAction>>,
3174
3175 #[serde(
3177 skip_serializing_if = "Option::is_none",
3178 deserialize_with = "one_or_many",
3179 default
3180 )]
3181 pub after_actions: Option<Vec<ExpectationAction>>,
3182
3183 #[serde(
3185 skip_serializing_if = "Option::is_none",
3186 deserialize_with = "one_or_many",
3187 default
3188 )]
3189 pub capture: Option<Vec<CaptureRule>>,
3190
3191 #[serde(skip_serializing_if = "Option::is_none")]
3193 pub namespace: Option<String>,
3194
3195 #[serde(skip_serializing_if = "Option::is_none")]
3197 pub steps: Option<Vec<ExpectationStep>>,
3198
3199 #[serde(skip_serializing_if = "Option::is_none")]
3201 pub timestamp: Option<String>,
3202
3203 #[serde(flatten, default)]
3207 pub extra: Extra,
3208}
3209
3210impl Expectation {
3211 pub fn new(request: HttpRequest) -> Self {
3213 Self {
3214 http_request: Some(request),
3215 ..Default::default()
3216 }
3217 }
3218
3219 pub fn id(mut self, id: impl Into<String>) -> Self {
3221 self.id = Some(id.into());
3222 self
3223 }
3224
3225 pub fn priority(mut self, priority: i32) -> Self {
3227 self.priority = Some(priority);
3228 self
3229 }
3230
3231 pub fn respond(mut self, response: HttpResponse) -> Self {
3233 self.http_response = Some(response);
3234 self
3235 }
3236
3237 pub fn forward(mut self, forward: HttpForward) -> Self {
3239 self.http_forward = Some(forward);
3240 self
3241 }
3242
3243 pub fn respond_template(mut self, template: HttpTemplate) -> Self {
3245 self.http_response_template = Some(template);
3246 self
3247 }
3248
3249 pub fn forward_template(mut self, template: HttpTemplate) -> Self {
3251 self.http_forward_template = Some(template);
3252 self
3253 }
3254
3255 pub fn error(mut self, error: HttpError) -> Self {
3257 self.http_error = Some(error);
3258 self
3259 }
3260
3261 pub fn respond_with_class_callback(mut self, callback_class: impl Into<String>) -> Self {
3268 self.http_response_class_callback = Some(HttpClassCallback::new(callback_class));
3269 self
3270 }
3271
3272 pub fn respond_class_callback(mut self, callback: HttpClassCallback) -> Self {
3274 self.http_response_class_callback = Some(callback);
3275 self
3276 }
3277
3278 pub fn forward_with_class_callback(mut self, callback_class: impl Into<String>) -> Self {
3280 self.http_forward_class_callback = Some(HttpClassCallback::new(callback_class));
3281 self
3282 }
3283
3284 pub fn forward_class_callback(mut self, callback: HttpClassCallback) -> Self {
3286 self.http_forward_class_callback = Some(callback);
3287 self
3288 }
3289
3290 pub fn respond_object_callback(mut self, callback: HttpObjectCallback) -> Self {
3297 self.http_response_object_callback = Some(callback);
3298 self
3299 }
3300
3301 pub fn forward_object_callback(mut self, callback: HttpObjectCallback) -> Self {
3303 self.http_forward_object_callback = Some(callback);
3304 self
3305 }
3306
3307 pub fn respond_sse(mut self, sse: HttpSseResponse) -> Self {
3309 self.http_sse_response = Some(sse);
3310 self
3311 }
3312
3313 pub fn respond_web_socket(mut self, ws: HttpWebSocketResponse) -> Self {
3315 self.http_web_socket_response = Some(ws);
3316 self
3317 }
3318
3319 pub fn respond_dns(mut self, dns: DnsResponse) -> Self {
3321 self.dns_response = Some(dns);
3322 self
3323 }
3324
3325 pub fn respond_binary(mut self, binary: BinaryResponse) -> Self {
3327 self.binary_response = Some(binary);
3328 self
3329 }
3330
3331 pub fn respond_grpc_stream(mut self, grpc: GrpcStreamResponse) -> Self {
3333 self.grpc_stream_response = Some(grpc);
3334 self
3335 }
3336
3337 pub fn times(mut self, times: Times) -> Self {
3339 self.times = Some(times);
3340 self
3341 }
3342
3343 pub fn time_to_live(mut self, ttl: TimeToLive) -> Self {
3345 self.time_to_live = Some(ttl);
3346 self
3347 }
3348
3349 pub fn scenario_name(mut self, name: impl Into<String>) -> Self {
3351 self.scenario_name = Some(name.into());
3352 self
3353 }
3354
3355 pub fn scenario_state(mut self, state: impl Into<String>) -> Self {
3357 self.scenario_state = Some(state.into());
3358 self
3359 }
3360
3361 pub fn new_scenario_state(mut self, state: impl Into<String>) -> Self {
3363 self.new_scenario_state = Some(state.into());
3364 self
3365 }
3366
3367 pub fn respond_with(mut self, response: HttpResponse) -> Self {
3372 self.http_responses
3373 .get_or_insert_with(Vec::new)
3374 .push(response);
3375 self
3376 }
3377
3378 pub fn http_responses(mut self, responses: Vec<HttpResponse>) -> Self {
3380 self.http_responses = Some(responses);
3381 self
3382 }
3383
3384 pub fn response_mode(mut self, mode: ResponseMode) -> Self {
3386 self.response_mode = Some(mode);
3387 self
3388 }
3389
3390 pub fn response_weights(mut self, weights: Vec<i32>) -> Self {
3392 self.response_weights = Some(weights);
3393 self
3394 }
3395
3396 pub fn switch_after(mut self, switch_after: i32) -> Self {
3399 self.switch_after = Some(switch_after);
3400 self
3401 }
3402
3403 pub fn cross_protocol_scenario(mut self, scenario: CrossProtocolScenario) -> Self {
3405 self.cross_protocol_scenarios
3406 .get_or_insert_with(Vec::new)
3407 .push(scenario);
3408 self
3409 }
3410
3411 pub fn cross_protocol_scenarios(mut self, scenarios: Vec<CrossProtocolScenario>) -> Self {
3413 self.cross_protocol_scenarios = Some(scenarios);
3414 self
3415 }
3416
3417 pub fn percentage(mut self, percentage: i32) -> Self {
3419 self.percentage = Some(percentage);
3420 self
3421 }
3422
3423 pub fn chaos(mut self, chaos: HttpChaosProfile) -> Self {
3425 self.chaos = Some(chaos);
3426 self
3427 }
3428
3429 pub fn rate_limit(mut self, rate_limit: RateLimit) -> Self {
3431 self.rate_limit = Some(rate_limit);
3432 self
3433 }
3434
3435 pub fn override_forwarded_request(
3437 mut self,
3438 override_request: HttpOverrideForwardedRequest,
3439 ) -> Self {
3440 self.http_override_forwarded_request = Some(override_request);
3441 self
3442 }
3443
3444 pub fn forward_validate(mut self, action: HttpForwardValidateAction) -> Self {
3446 self.http_forward_validate_action = Some(action);
3447 self
3448 }
3449
3450 pub fn forward_with_fallback(mut self, action: HttpForwardWithFallback) -> Self {
3452 self.http_forward_with_fallback = Some(action);
3453 self
3454 }
3455
3456 pub fn respond_llm(mut self, llm: HttpLlmResponse) -> Self {
3458 self.http_llm_response = Some(llm);
3459 self
3460 }
3461
3462 pub fn respond_grpc_bidi(mut self, grpc: GrpcBidiResponse) -> Self {
3464 self.grpc_bidi_response = Some(grpc);
3465 self
3466 }
3467
3468 pub fn before_action(mut self, action: ExpectationAction) -> Self {
3470 self.before_actions
3471 .get_or_insert_with(Vec::new)
3472 .push(action);
3473 self
3474 }
3475
3476 pub fn after_action(mut self, action: ExpectationAction) -> Self {
3478 self.after_actions.get_or_insert_with(Vec::new).push(action);
3479 self
3480 }
3481
3482 pub fn capture_rule(mut self, rule: CaptureRule) -> Self {
3484 self.capture.get_or_insert_with(Vec::new).push(rule);
3485 self
3486 }
3487
3488 pub fn namespace(mut self, namespace: impl Into<String>) -> Self {
3490 self.namespace = Some(namespace.into());
3491 self
3492 }
3493
3494 pub fn step(mut self, step: ExpectationStep) -> Self {
3496 self.steps.get_or_insert_with(Vec::new).push(step);
3497 self
3498 }
3499
3500 pub fn steps(mut self, steps: Vec<ExpectationStep>) -> Self {
3502 self.steps = Some(steps);
3503 self
3504 }
3505}
3506
3507#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
3518#[serde(rename_all = "camelCase")]
3519pub struct Verification {
3520 #[serde(skip_serializing_if = "Option::is_none")]
3521 pub http_request: Option<HttpRequest>,
3522
3523 #[serde(skip_serializing_if = "Option::is_none")]
3524 pub http_response: Option<HttpResponse>,
3525
3526 #[serde(skip_serializing_if = "Option::is_none")]
3527 pub times: Option<VerificationTimes>,
3528
3529 #[serde(skip_serializing_if = "Option::is_none")]
3530 pub maximum_number_of_request_to_return_in_verification_failure: Option<u32>,
3531}
3532
3533#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
3539#[serde(rename_all = "camelCase")]
3540pub struct VerificationSequence {
3541 #[serde(skip_serializing_if = "Option::is_none")]
3542 pub http_requests: Option<Vec<HttpRequest>>,
3543
3544 #[serde(skip_serializing_if = "Option::is_none")]
3545 pub http_responses: Option<Vec<HttpResponse>>,
3546}
3547
3548#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
3554pub struct Ports {
3555 pub ports: Vec<u16>,
3556}
3557
3558#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3565#[serde(rename_all = "camelCase")]
3566pub struct ScenarioState {
3567 pub scenario_name: String,
3569 pub current_state: String,
3571}
3572
3573#[derive(Debug, Clone, Deserialize)]
3576pub(crate) struct ScenarioList {
3577 #[serde(default)]
3578 pub scenarios: Vec<ScenarioState>,
3579}
3580
3581#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3587pub enum RetrieveType {
3588 Requests,
3590 ActiveExpectations,
3592 RecordedExpectations,
3594 Logs,
3596 RequestResponses,
3598}
3599
3600impl RetrieveType {
3601 pub fn as_str(&self) -> &'static str {
3603 match self {
3604 RetrieveType::Requests => "REQUESTS",
3605 RetrieveType::ActiveExpectations => "ACTIVE_EXPECTATIONS",
3606 RetrieveType::RecordedExpectations => "RECORDED_EXPECTATIONS",
3607 RetrieveType::Logs => "LOGS",
3608 RetrieveType::RequestResponses => "REQUEST_RESPONSES",
3609 }
3610 }
3611}
3612
3613#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3619pub enum RetrieveFormat {
3620 Json,
3621 LogEntries,
3622 Java,
3623 JavaScript,
3624 Python,
3625 Go,
3626 CSharp,
3627 Ruby,
3628 Rust,
3629 Php,
3630}
3631
3632impl RetrieveFormat {
3633 pub fn as_str(&self) -> &'static str {
3635 match self {
3636 RetrieveFormat::Json => "JSON",
3637 RetrieveFormat::LogEntries => "LOG_ENTRIES",
3638 RetrieveFormat::Java => "JAVA",
3639 RetrieveFormat::JavaScript => "JAVASCRIPT",
3640 RetrieveFormat::Python => "PYTHON",
3641 RetrieveFormat::Go => "GO",
3642 RetrieveFormat::CSharp => "CSHARP",
3643 RetrieveFormat::Ruby => "RUBY",
3644 RetrieveFormat::Rust => "RUST",
3645 RetrieveFormat::Php => "PHP",
3646 }
3647 }
3648}
3649
3650#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3652pub enum ClearType {
3653 All,
3654 Log,
3655 Expectations,
3656}
3657
3658impl ClearType {
3659 pub fn as_str(&self) -> &'static str {
3661 match self {
3662 ClearType::All => "ALL",
3663 ClearType::Log => "LOG",
3664 ClearType::Expectations => "EXPECTATIONS",
3665 }
3666 }
3667}
3668
3669#[derive(Debug, Clone, PartialEq, Eq)]
3679pub struct PactVerification {
3680 pub passed: bool,
3682 pub report: String,
3684}
3685
3686#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3704pub enum MockMode {
3705 Simulate,
3707 Spy,
3709 Capture,
3711}
3712
3713impl MockMode {
3714 pub fn as_str(&self) -> &'static str {
3716 match self {
3717 MockMode::Simulate => "SIMULATE",
3718 MockMode::Spy => "SPY",
3719 MockMode::Capture => "CAPTURE",
3720 }
3721 }
3722
3723 pub fn proxy_unmatched_requests(&self) -> bool {
3726 !matches!(self, MockMode::Simulate)
3727 }
3728}
3729
3730impl std::fmt::Display for MockMode {
3731 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3732 f.write_str(self.as_str())
3733 }
3734}
3735
3736impl std::str::FromStr for MockMode {
3737 type Err = String;
3738
3739 fn from_str(value: &str) -> std::result::Result<Self, Self::Err> {
3742 match value.trim().to_uppercase().as_str() {
3743 "" => Err("mode is required (one of SIMULATE, SPY, CAPTURE)".to_string()),
3744 "SIMULATE" => Ok(MockMode::Simulate),
3745 "SPY" => Ok(MockMode::Spy),
3746 "CAPTURE" => Ok(MockMode::Capture),
3747 other => Err(format!(
3748 "unknown mode '{other}' (expected one of SIMULATE, SPY, CAPTURE)"
3749 )),
3750 }
3751 }
3752}
3753
3754#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
3766#[serde(rename_all = "camelCase")]
3767pub struct GrpcMethod {
3768 pub name: String,
3770
3771 pub input_type: String,
3773
3774 pub output_type: String,
3776
3777 pub client_streaming: bool,
3779
3780 pub server_streaming: bool,
3782}
3783
3784#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
3791#[serde(rename_all = "camelCase")]
3792pub struct GrpcService {
3793 pub name: String,
3795
3796 pub methods: Vec<GrpcMethod>,
3798}
3799
3800#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
3809#[serde(rename_all = "camelCase")]
3810pub struct SocketAddress {
3811 pub host: String,
3813
3814 pub port: u16,
3816
3817 #[serde(skip_serializing_if = "Option::is_none")]
3820 pub scheme: Option<String>,
3821}
3822
3823impl SocketAddress {
3824 pub fn new(host: impl Into<String>, port: u16) -> Self {
3826 Self {
3827 host: host.into(),
3828 port,
3829 scheme: None,
3830 }
3831 }
3832
3833 pub fn scheme(mut self, scheme: impl Into<String>) -> Self {
3835 self.scheme = Some(scheme.into());
3836 self
3837 }
3838
3839 pub fn https(host: impl Into<String>, port: u16) -> Self {
3841 Self::new(host, port).scheme("HTTPS")
3842 }
3843}
3844
3845#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
3854#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
3855pub enum RampCurve {
3856 Linear,
3858 Quadratic,
3860 Exponential,
3862}
3863
3864#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
3870#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
3871pub enum LoadStageType {
3872 Vu,
3874 Rate,
3876 Pause,
3878}
3879
3880#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
3889#[serde(rename_all = "camelCase")]
3890pub struct LoadStage {
3891 #[serde(rename = "type")]
3893 pub stage_type: LoadStageType,
3894
3895 pub duration_millis: u64,
3897
3898 #[serde(skip_serializing_if = "Option::is_none")]
3900 pub curve: Option<RampCurve>,
3901
3902 #[serde(skip_serializing_if = "Option::is_none")]
3904 pub vus: Option<u32>,
3905
3906 #[serde(skip_serializing_if = "Option::is_none")]
3908 pub start_vus: Option<u32>,
3909
3910 #[serde(skip_serializing_if = "Option::is_none")]
3912 pub end_vus: Option<u32>,
3913
3914 #[serde(skip_serializing_if = "Option::is_none")]
3916 pub rate: Option<f64>,
3917
3918 #[serde(skip_serializing_if = "Option::is_none")]
3920 pub start_rate: Option<f64>,
3921
3922 #[serde(skip_serializing_if = "Option::is_none")]
3924 pub end_rate: Option<f64>,
3925
3926 #[serde(skip_serializing_if = "Option::is_none")]
3928 pub max_vus: Option<u32>,
3929}
3930
3931impl LoadStage {
3932 fn base(stage_type: LoadStageType, duration_millis: u64) -> Self {
3933 Self {
3934 stage_type,
3935 duration_millis,
3936 curve: None,
3937 vus: None,
3938 start_vus: None,
3939 end_vus: None,
3940 rate: None,
3941 start_rate: None,
3942 end_rate: None,
3943 max_vus: None,
3944 }
3945 }
3946
3947 pub fn vu_hold(vus: u32, duration_millis: u64) -> Self {
3949 let mut stage = Self::base(LoadStageType::Vu, duration_millis);
3950 stage.vus = Some(vus);
3951 stage
3952 }
3953
3954 pub fn vu_ramp(start_vus: u32, end_vus: u32, duration_millis: u64, curve: RampCurve) -> Self {
3957 let mut stage = Self::base(LoadStageType::Vu, duration_millis);
3958 stage.start_vus = Some(start_vus);
3959 stage.end_vus = Some(end_vus);
3960 stage.curve = Some(curve);
3961 stage
3962 }
3963
3964 pub fn rate_hold(rate: f64, duration_millis: u64) -> Self {
3966 let mut stage = Self::base(LoadStageType::Rate, duration_millis);
3967 stage.rate = Some(rate);
3968 stage
3969 }
3970
3971 pub fn rate_ramp(
3974 start_rate: f64,
3975 end_rate: f64,
3976 duration_millis: u64,
3977 curve: RampCurve,
3978 ) -> Self {
3979 let mut stage = Self::base(LoadStageType::Rate, duration_millis);
3980 stage.start_rate = Some(start_rate);
3981 stage.end_rate = Some(end_rate);
3982 stage.curve = Some(curve);
3983 stage
3984 }
3985
3986 pub fn pause(duration_millis: u64) -> Self {
3988 Self::base(LoadStageType::Pause, duration_millis)
3989 }
3990
3991 pub fn max_vus(mut self, max_vus: u32) -> Self {
3993 self.max_vus = Some(max_vus);
3994 self
3995 }
3996}
3997
3998#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
4001#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
4002pub enum LoadShapeType {
4003 Spike,
4005 Stairs,
4007 RampHold,
4009}
4010
4011#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
4016#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
4017pub enum LoadShapeMetric {
4018 Vu,
4020 Rate,
4022}
4023
4024#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
4033#[serde(rename_all = "camelCase")]
4034pub struct LoadShape {
4035 #[serde(rename = "type")]
4037 pub shape_type: LoadShapeType,
4038
4039 #[serde(skip_serializing_if = "Option::is_none")]
4041 pub metric: Option<LoadShapeMetric>,
4042
4043 #[serde(skip_serializing_if = "Option::is_none")]
4045 pub curve: Option<RampCurve>,
4046
4047 #[serde(skip_serializing_if = "Option::is_none")]
4049 pub baseline: Option<f64>,
4050
4051 #[serde(skip_serializing_if = "Option::is_none")]
4053 pub peak: Option<f64>,
4054
4055 #[serde(skip_serializing_if = "Option::is_none")]
4057 pub ramp_up_millis: Option<u64>,
4058
4059 #[serde(skip_serializing_if = "Option::is_none")]
4062 pub hold_millis: Option<u64>,
4063
4064 #[serde(skip_serializing_if = "Option::is_none")]
4066 pub ramp_down_millis: Option<u64>,
4067
4068 #[serde(skip_serializing_if = "Option::is_none")]
4070 pub recovery_hold_millis: Option<u64>,
4071
4072 #[serde(skip_serializing_if = "Option::is_none")]
4074 pub start: Option<f64>,
4075
4076 #[serde(skip_serializing_if = "Option::is_none")]
4078 pub step: Option<f64>,
4079
4080 #[serde(skip_serializing_if = "Option::is_none")]
4082 pub steps: Option<u32>,
4083
4084 #[serde(skip_serializing_if = "Option::is_none")]
4086 pub step_duration_millis: Option<u64>,
4087
4088 #[serde(skip_serializing_if = "Option::is_none")]
4090 pub target: Option<f64>,
4091
4092 #[serde(skip_serializing_if = "Option::is_none")]
4094 pub ramp_millis: Option<u64>,
4095}
4096
4097impl LoadShape {
4098 fn base(shape_type: LoadShapeType) -> Self {
4099 Self {
4100 shape_type,
4101 metric: None,
4102 curve: None,
4103 baseline: None,
4104 peak: None,
4105 ramp_up_millis: None,
4106 hold_millis: None,
4107 ramp_down_millis: None,
4108 recovery_hold_millis: None,
4109 start: None,
4110 step: None,
4111 steps: None,
4112 step_duration_millis: None,
4113 target: None,
4114 ramp_millis: None,
4115 }
4116 }
4117
4118 pub fn spike(
4121 baseline: f64,
4122 peak: f64,
4123 ramp_up_millis: u64,
4124 hold_millis: u64,
4125 ramp_down_millis: u64,
4126 ) -> Self {
4127 let mut shape = Self::base(LoadShapeType::Spike);
4128 shape.baseline = Some(baseline);
4129 shape.peak = Some(peak);
4130 shape.ramp_up_millis = Some(ramp_up_millis);
4131 shape.hold_millis = Some(hold_millis);
4132 shape.ramp_down_millis = Some(ramp_down_millis);
4133 shape
4134 }
4135
4136 pub fn stairs(start: f64, step: f64, steps: u32, step_duration_millis: u64) -> Self {
4139 let mut shape = Self::base(LoadShapeType::Stairs);
4140 shape.start = Some(start);
4141 shape.step = Some(step);
4142 shape.steps = Some(steps);
4143 shape.step_duration_millis = Some(step_duration_millis);
4144 shape
4145 }
4146
4147 pub fn ramp_hold(target: f64, ramp_millis: u64, hold_millis: u64) -> Self {
4150 let mut shape = Self::base(LoadShapeType::RampHold);
4151 shape.target = Some(target);
4152 shape.ramp_millis = Some(ramp_millis);
4153 shape.hold_millis = Some(hold_millis);
4154 shape
4155 }
4156
4157 pub fn metric(mut self, metric: LoadShapeMetric) -> Self {
4159 self.metric = Some(metric);
4160 self
4161 }
4162
4163 pub fn curve(mut self, curve: RampCurve) -> Self {
4165 self.curve = Some(curve);
4166 self
4167 }
4168
4169 pub fn recovery_hold_millis(mut self, recovery_hold_millis: u64) -> Self {
4172 self.recovery_hold_millis = Some(recovery_hold_millis);
4173 self
4174 }
4175}
4176
4177#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
4180#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
4181pub enum LoadThresholdMetric {
4182 LatencyP50,
4184 LatencyP95,
4186 LatencyP99,
4188 LatencyP999,
4190 ErrorRate,
4192 ThroughputRps,
4194}
4195
4196#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
4199#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
4200pub enum LoadComparator {
4201 LessThan,
4203 LessThanOrEqual,
4205 GreaterThan,
4207 GreaterThanOrEqual,
4209}
4210
4211#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
4215#[serde(rename_all = "camelCase")]
4216pub struct LoadThreshold {
4217 pub metric: LoadThresholdMetric,
4219
4220 pub comparator: LoadComparator,
4222
4223 pub threshold: f64,
4226}
4227
4228impl LoadThreshold {
4229 pub fn new(metric: LoadThresholdMetric, comparator: LoadComparator, threshold: f64) -> Self {
4231 Self {
4232 metric,
4233 comparator,
4234 threshold,
4235 }
4236 }
4237}
4238
4239#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
4242#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
4243pub enum LoadPacingMode {
4244 None,
4246 ConstantPacing,
4248 ConstantThroughput,
4250}
4251
4252#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
4256#[serde(rename_all = "camelCase")]
4257pub struct LoadPacing {
4258 pub mode: LoadPacingMode,
4260
4261 pub value: f64,
4265}
4266
4267impl LoadPacing {
4268 pub fn new(mode: LoadPacingMode, value: f64) -> Self {
4270 Self { mode, value }
4271 }
4272
4273 pub fn constant_pacing(cycle_millis: f64) -> Self {
4275 Self::new(LoadPacingMode::ConstantPacing, cycle_millis)
4276 }
4277
4278 pub fn constant_throughput(iterations_per_second: f64) -> Self {
4280 Self::new(LoadPacingMode::ConstantThroughput, iterations_per_second)
4281 }
4282}
4283
4284#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
4287#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
4288pub enum LoadFeederFormat {
4289 Csv,
4291 Json,
4293}
4294
4295#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
4298#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
4299pub enum LoadFeederStrategy {
4300 Circular,
4302 Random,
4304 Sequential,
4306}
4307
4308#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
4313#[serde(rename_all = "camelCase")]
4314pub struct LoadFeeder {
4315 #[serde(skip_serializing_if = "Vec::is_empty")]
4317 pub rows: Vec<HashMap<String, String>>,
4318
4319 #[serde(skip_serializing_if = "Option::is_none")]
4321 pub data: Option<String>,
4322
4323 #[serde(skip_serializing_if = "Option::is_none")]
4325 pub format: Option<LoadFeederFormat>,
4326
4327 #[serde(skip_serializing_if = "Option::is_none")]
4329 pub strategy: Option<LoadFeederStrategy>,
4330}
4331
4332impl LoadFeeder {
4333 pub fn rows(rows: Vec<HashMap<String, String>>) -> Self {
4335 Self {
4336 rows,
4337 ..Self::default()
4338 }
4339 }
4340
4341 pub fn data(data: impl Into<String>, format: LoadFeederFormat) -> Self {
4343 Self {
4344 data: Some(data.into()),
4345 format: Some(format),
4346 ..Self::default()
4347 }
4348 }
4349
4350 pub fn strategy(mut self, strategy: LoadFeederStrategy) -> Self {
4352 self.strategy = Some(strategy);
4353 self
4354 }
4355}
4356
4357#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
4360#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
4361pub enum LoadCaptureSource {
4362 BodyJsonpath,
4364 Header,
4366 BodyRegex,
4368}
4369
4370#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
4375#[serde(rename_all = "camelCase")]
4376pub struct LoadCapture {
4377 pub name: String,
4379
4380 pub source: LoadCaptureSource,
4382
4383 pub expression: String,
4385
4386 #[serde(skip_serializing_if = "Option::is_none")]
4388 pub default_value: Option<String>,
4389}
4390
4391impl LoadCapture {
4392 pub fn new(
4395 name: impl Into<String>,
4396 source: LoadCaptureSource,
4397 expression: impl Into<String>,
4398 ) -> Self {
4399 Self {
4400 name: name.into(),
4401 source,
4402 expression: expression.into(),
4403 default_value: None,
4404 }
4405 }
4406
4407 pub fn default_value(mut self, default_value: impl Into<String>) -> Self {
4409 self.default_value = Some(default_value.into());
4410 self
4411 }
4412}
4413
4414#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
4417#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
4418pub enum LoadStepSelection {
4419 Sequential,
4421 Weighted,
4423}
4424
4425#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
4433#[serde(rename_all = "camelCase")]
4434pub struct LoadProfile {
4435 #[serde(skip_serializing_if = "Vec::is_empty")]
4438 pub stages: Vec<LoadStage>,
4439
4440 #[serde(skip_serializing_if = "Option::is_none")]
4443 pub shape: Option<LoadShape>,
4444}
4445
4446impl LoadProfile {
4447 pub fn of(stages: Vec<LoadStage>) -> Self {
4449 Self {
4450 stages,
4451 shape: None,
4452 }
4453 }
4454
4455 pub fn shaped(shape: LoadShape) -> Self {
4457 Self {
4458 stages: Vec::new(),
4459 shape: Some(shape),
4460 }
4461 }
4462
4463 pub fn constant(vus: u32, duration_millis: u64) -> Self {
4465 Self::of(vec![LoadStage::vu_hold(vus, duration_millis)])
4466 }
4467
4468 pub fn linear(start_vus: u32, end_vus: u32, duration_millis: u64) -> Self {
4471 Self::of(vec![LoadStage::vu_ramp(
4472 start_vus,
4473 end_vus,
4474 duration_millis,
4475 RampCurve::Linear,
4476 )])
4477 }
4478
4479 pub fn add_stage(mut self, stage: LoadStage) -> Self {
4481 self.stages.push(stage);
4482 self
4483 }
4484}
4485
4486#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
4489#[serde(rename_all = "camelCase")]
4490pub struct LoadStep {
4491 pub request: HttpRequest,
4493
4494 #[serde(skip_serializing_if = "Option::is_none")]
4496 pub think_time: Option<Delay>,
4497
4498 #[serde(skip_serializing_if = "Vec::is_empty")]
4502 pub captures: Vec<LoadCapture>,
4503
4504 #[serde(skip_serializing_if = "Option::is_none")]
4508 pub weight: Option<f64>,
4509}
4510
4511impl LoadStep {
4512 pub fn new(request: HttpRequest) -> Self {
4514 Self {
4515 request,
4516 think_time: None,
4517 captures: Vec::new(),
4518 weight: None,
4519 }
4520 }
4521
4522 pub fn think_time(mut self, delay: Delay) -> Self {
4524 self.think_time = Some(delay);
4525 self
4526 }
4527
4528 pub fn capture(mut self, capture: LoadCapture) -> Self {
4530 self.captures.push(capture);
4531 self
4532 }
4533
4534 pub fn weight(mut self, weight: f64) -> Self {
4537 self.weight = Some(weight);
4538 self
4539 }
4540}
4541
4542#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
4549#[serde(rename_all = "camelCase")]
4550pub struct LoadScenario {
4551 pub name: String,
4553
4554 #[serde(skip_serializing_if = "Option::is_none")]
4557 pub template_type: Option<String>,
4558
4559 #[serde(skip_serializing_if = "Option::is_none")]
4561 pub max_requests: Option<u64>,
4562
4563 #[serde(skip_serializing_if = "Option::is_none")]
4567 pub start_delay_millis: Option<u64>,
4568
4569 #[serde(skip_serializing_if = "Vec::is_empty")]
4572 pub thresholds: Vec<LoadThreshold>,
4573
4574 #[serde(skip_serializing_if = "std::ops::Not::not")]
4576 pub abort_on_fail: bool,
4577
4578 #[serde(skip_serializing_if = "Option::is_none")]
4581 pub abort_grace_millis: Option<u64>,
4582
4583 #[serde(skip_serializing_if = "Option::is_none")]
4585 pub pacing: Option<LoadPacing>,
4586
4587 #[serde(skip_serializing_if = "Option::is_none")]
4589 pub feeder: Option<LoadFeeder>,
4590
4591 #[serde(skip_serializing_if = "Option::is_none")]
4594 pub step_selection: Option<LoadStepSelection>,
4595
4596 pub profile: LoadProfile,
4598
4599 pub steps: Vec<LoadStep>,
4601}
4602
4603impl LoadScenario {
4604 pub fn new(name: impl Into<String>, profile: LoadProfile, steps: Vec<LoadStep>) -> Self {
4606 Self {
4607 name: name.into(),
4608 template_type: None,
4609 max_requests: None,
4610 start_delay_millis: None,
4611 thresholds: Vec::new(),
4612 abort_on_fail: false,
4613 abort_grace_millis: None,
4614 pacing: None,
4615 feeder: None,
4616 step_selection: None,
4617 profile,
4618 steps,
4619 }
4620 }
4621
4622 pub fn threshold(mut self, threshold: LoadThreshold) -> Self {
4624 self.thresholds.push(threshold);
4625 self
4626 }
4627
4628 pub fn abort_on_fail(mut self, abort_on_fail: bool) -> Self {
4630 self.abort_on_fail = abort_on_fail;
4631 self
4632 }
4633
4634 pub fn abort_grace_millis(mut self, abort_grace_millis: u64) -> Self {
4636 self.abort_grace_millis = Some(abort_grace_millis);
4637 self
4638 }
4639
4640 pub fn pacing(mut self, pacing: LoadPacing) -> Self {
4642 self.pacing = Some(pacing);
4643 self
4644 }
4645
4646 pub fn feeder(mut self, feeder: LoadFeeder) -> Self {
4648 self.feeder = Some(feeder);
4649 self
4650 }
4651
4652 pub fn step_selection(mut self, step_selection: LoadStepSelection) -> Self {
4654 self.step_selection = Some(step_selection);
4655 self
4656 }
4657
4658 pub fn template_type(mut self, template_type: impl Into<String>) -> Self {
4660 self.template_type = Some(template_type.into());
4661 self
4662 }
4663
4664 pub fn max_requests(mut self, max_requests: u64) -> Self {
4666 self.max_requests = Some(max_requests);
4667 self
4668 }
4669
4670 pub fn start_delay_millis(mut self, start_delay_millis: u64) -> Self {
4673 self.start_delay_millis = Some(start_delay_millis);
4674 self
4675 }
4676}
4677
4678#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
4685#[serde(rename_all = "camelCase")]
4686pub struct SloObjective {
4687 pub sli: String,
4690
4691 pub comparator: String,
4695
4696 pub threshold: f64,
4699
4700 #[serde(skip_serializing_if = "Option::is_none")]
4703 pub scope: Option<String>,
4704}
4705
4706impl SloObjective {
4707 pub fn new(sli: impl Into<String>, comparator: impl Into<String>, threshold: f64) -> Self {
4709 Self {
4710 sli: sli.into(),
4711 comparator: comparator.into(),
4712 threshold,
4713 scope: None,
4714 }
4715 }
4716
4717 pub fn scope(mut self, scope: impl Into<String>) -> Self {
4719 self.scope = Some(scope.into());
4720 self
4721 }
4722}
4723
4724#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
4727#[serde(rename_all = "camelCase")]
4728pub struct SloWindow {
4729 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
4731 pub window_type: Option<String>,
4732
4733 #[serde(skip_serializing_if = "Option::is_none")]
4735 pub lookback_millis: Option<u64>,
4736
4737 #[serde(skip_serializing_if = "Option::is_none")]
4739 pub from_epoch_millis: Option<u64>,
4740
4741 #[serde(skip_serializing_if = "Option::is_none")]
4743 pub to_epoch_millis: Option<u64>,
4744}
4745
4746impl SloWindow {
4747 pub fn lookback(millis: u64) -> Self {
4749 Self {
4750 window_type: Some("LOOKBACK".to_string()),
4751 lookback_millis: Some(millis),
4752 ..Default::default()
4753 }
4754 }
4755
4756 pub fn explicit(from_epoch_millis: u64, to_epoch_millis: u64) -> Self {
4758 Self {
4759 window_type: Some("EXPLICIT".to_string()),
4760 from_epoch_millis: Some(from_epoch_millis),
4761 to_epoch_millis: Some(to_epoch_millis),
4762 ..Default::default()
4763 }
4764 }
4765}
4766
4767#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
4770#[serde(rename_all = "camelCase")]
4771pub struct SloCriteria {
4772 #[serde(skip_serializing_if = "Option::is_none")]
4774 pub name: Option<String>,
4775
4776 #[serde(skip_serializing_if = "Option::is_none")]
4778 pub window: Option<SloWindow>,
4779
4780 #[serde(skip_serializing_if = "Option::is_none")]
4783 pub minimum_sample_count: Option<u64>,
4784
4785 #[serde(skip_serializing_if = "Option::is_none")]
4787 pub upstream_hosts: Option<Vec<String>>,
4788
4789 pub objectives: Vec<SloObjective>,
4791}
4792
4793impl SloCriteria {
4794 pub fn new(objectives: Vec<SloObjective>) -> Self {
4796 Self {
4797 name: None,
4798 window: None,
4799 minimum_sample_count: None,
4800 upstream_hosts: None,
4801 objectives,
4802 }
4803 }
4804
4805 pub fn name(mut self, name: impl Into<String>) -> Self {
4807 self.name = Some(name.into());
4808 self
4809 }
4810
4811 pub fn window(mut self, window: SloWindow) -> Self {
4813 self.window = Some(window);
4814 self
4815 }
4816
4817 pub fn minimum_sample_count(mut self, count: u64) -> Self {
4819 self.minimum_sample_count = Some(count);
4820 self
4821 }
4822
4823 pub fn upstream_hosts(mut self, hosts: Vec<String>) -> Self {
4825 self.upstream_hosts = Some(hosts);
4826 self
4827 }
4828}
4829
4830#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
4833#[serde(rename_all = "camelCase")]
4834pub struct SloObjectiveResult {
4835 #[serde(skip_serializing_if = "Option::is_none")]
4836 pub sli: Option<String>,
4837 #[serde(skip_serializing_if = "Option::is_none")]
4838 pub comparator: Option<String>,
4839 #[serde(skip_serializing_if = "Option::is_none")]
4840 pub threshold: Option<f64>,
4841 #[serde(skip_serializing_if = "Option::is_none")]
4842 pub observed_value: Option<f64>,
4843 #[serde(skip_serializing_if = "Option::is_none")]
4845 pub result: Option<String>,
4846 #[serde(skip_serializing_if = "Option::is_none")]
4847 pub detail: Option<String>,
4848}
4849
4850#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
4853#[serde(rename_all = "camelCase")]
4854pub struct SloVerdict {
4855 #[serde(skip_serializing_if = "Option::is_none")]
4856 pub name: Option<String>,
4857 #[serde(skip_serializing_if = "Option::is_none")]
4859 pub result: Option<String>,
4860 #[serde(skip_serializing_if = "Option::is_none")]
4861 pub window_from_epoch_millis: Option<u64>,
4862 #[serde(skip_serializing_if = "Option::is_none")]
4863 pub window_to_epoch_millis: Option<u64>,
4864 #[serde(skip_serializing_if = "Option::is_none")]
4865 pub sample_count: Option<u64>,
4866 #[serde(default, skip_serializing_if = "Vec::is_empty")]
4867 pub objective_results: Vec<SloObjectiveResult>,
4868}
4869
4870impl SloVerdict {
4871 pub fn is_pass(&self) -> bool {
4873 self.result.as_deref() == Some("PASS")
4874 }
4875
4876 pub fn is_fail(&self) -> bool {
4878 self.result.as_deref() == Some("FAIL")
4879 }
4880
4881 pub fn is_inconclusive(&self) -> bool {
4883 self.result.as_deref() == Some("INCONCLUSIVE")
4884 }
4885}
4886
4887#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
4894#[serde(rename_all = "camelCase")]
4895pub struct PreemptionRequest {
4896 #[serde(skip_serializing_if = "Option::is_none")]
4899 pub mode: Option<String>,
4900
4901 #[serde(skip_serializing_if = "Option::is_none")]
4903 pub drain_millis: Option<u64>,
4904
4905 #[serde(skip_serializing_if = "Option::is_none")]
4908 pub ttl_millis: Option<u64>,
4909
4910 #[serde(skip_serializing_if = "Option::is_none")]
4913 pub last_stream_id: Option<i64>,
4914}
4915
4916impl PreemptionRequest {
4917 pub fn new() -> Self {
4919 Self::default()
4920 }
4921
4922 pub fn mode(mut self, mode: impl Into<String>) -> Self {
4924 self.mode = Some(mode.into());
4925 self
4926 }
4927
4928 pub fn drain_millis(mut self, millis: u64) -> Self {
4930 self.drain_millis = Some(millis);
4931 self
4932 }
4933
4934 pub fn ttl_millis(mut self, millis: u64) -> Self {
4936 self.ttl_millis = Some(millis);
4937 self
4938 }
4939
4940 pub fn last_stream_id(mut self, id: i64) -> Self {
4942 self.last_stream_id = Some(id);
4943 self
4944 }
4945}
4946
4947#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
4950#[serde(rename_all = "camelCase")]
4951pub struct PreemptionStatus {
4952 #[serde(skip_serializing_if = "Option::is_none")]
4954 pub state: Option<String>,
4955
4956 #[serde(skip_serializing_if = "Option::is_none")]
4958 pub in_flight: Option<u64>,
4959
4960 #[serde(skip_serializing_if = "Option::is_none")]
4962 pub drain_remaining_millis: Option<u64>,
4963
4964 #[serde(skip_serializing_if = "Option::is_none")]
4966 pub mode: Option<String>,
4967}
4968
4969#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
4977#[serde(rename_all = "camelCase")]
4978pub struct HttpChaosProfile {
4979 #[serde(skip_serializing_if = "Option::is_none")]
4981 pub error_status: Option<u16>,
4982
4983 #[serde(skip_serializing_if = "Option::is_none")]
4985 pub error_probability: Option<f64>,
4986
4987 #[serde(skip_serializing_if = "Option::is_none")]
4989 pub latency: Option<Delay>,
4990
4991 #[serde(skip_serializing_if = "Option::is_none")]
4993 pub connection_drop: Option<bool>,
4994
4995 #[serde(skip_serializing_if = "Option::is_none")]
4997 pub seed: Option<i64>,
4998
4999 #[serde(skip_serializing_if = "Option::is_none")]
5001 pub retry_after: Option<String>,
5002
5003 #[serde(skip_serializing_if = "Option::is_none")]
5005 pub drop_connection_probability: Option<f64>,
5006
5007 #[serde(skip_serializing_if = "Option::is_none")]
5009 pub succeed_first: Option<i64>,
5010
5011 #[serde(skip_serializing_if = "Option::is_none")]
5013 pub fail_request_count: Option<i64>,
5014
5015 #[serde(skip_serializing_if = "Option::is_none")]
5017 pub outage_after_millis: Option<i64>,
5018
5019 #[serde(skip_serializing_if = "Option::is_none")]
5021 pub outage_duration_millis: Option<i64>,
5022
5023 #[serde(skip_serializing_if = "Option::is_none")]
5025 pub truncate_body_at_fraction: Option<f64>,
5026
5027 #[serde(skip_serializing_if = "Option::is_none")]
5029 pub malformed_body: Option<bool>,
5030
5031 #[serde(skip_serializing_if = "Option::is_none")]
5033 pub slow_response_chunk_size: Option<i64>,
5034
5035 #[serde(skip_serializing_if = "Option::is_none")]
5037 pub slow_response_chunk_delay: Option<Delay>,
5038
5039 #[serde(skip_serializing_if = "Option::is_none")]
5041 pub quota_name: Option<String>,
5042
5043 #[serde(skip_serializing_if = "Option::is_none")]
5045 pub quota_limit: Option<i64>,
5046
5047 #[serde(skip_serializing_if = "Option::is_none")]
5049 pub quota_window_millis: Option<i64>,
5050
5051 #[serde(skip_serializing_if = "Option::is_none")]
5053 pub quota_error_status: Option<u16>,
5054
5055 #[serde(skip_serializing_if = "Option::is_none")]
5057 pub degradation_ramp_millis: Option<i64>,
5058
5059 #[serde(skip_serializing_if = "Option::is_none")]
5061 pub graphql_errors: Option<bool>,
5062
5063 #[serde(skip_serializing_if = "Option::is_none")]
5065 pub graphql_error_message: Option<String>,
5066
5067 #[serde(skip_serializing_if = "Option::is_none")]
5069 pub graphql_error_code: Option<String>,
5070
5071 #[serde(skip_serializing_if = "Option::is_none")]
5073 pub graphql_nullify_data: Option<bool>,
5074
5075 #[serde(flatten)]
5077 pub extra: HashMap<String, serde_json::Value>,
5078}
5079
5080impl HttpChaosProfile {
5081 pub fn new() -> Self {
5083 Self::default()
5084 }
5085
5086 pub fn error_status(mut self, status: u16) -> Self {
5088 self.error_status = Some(status);
5089 self
5090 }
5091
5092 pub fn error_probability(mut self, probability: f64) -> Self {
5094 self.error_probability = Some(probability);
5095 self
5096 }
5097
5098 pub fn latency(mut self, latency: Delay) -> Self {
5100 self.latency = Some(latency);
5101 self
5102 }
5103
5104 pub fn connection_drop(mut self, drop: bool) -> Self {
5106 self.connection_drop = Some(drop);
5107 self
5108 }
5109
5110 pub fn seed(mut self, seed: i64) -> Self {
5112 self.seed = Some(seed);
5113 self
5114 }
5115}
5116
5117#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
5124#[serde(rename_all = "camelCase")]
5125pub struct ChaosStage {
5126 pub duration_millis: u64,
5128
5129 pub profiles: HashMap<String, HttpChaosProfile>,
5131}
5132
5133impl ChaosStage {
5134 pub fn new(duration_millis: u64) -> Self {
5136 Self {
5137 duration_millis,
5138 profiles: HashMap::new(),
5139 }
5140 }
5141
5142 pub fn profile(mut self, host: impl Into<String>, profile: HttpChaosProfile) -> Self {
5144 self.profiles.insert(host.into(), profile);
5145 self
5146 }
5147}
5148
5149#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5152#[serde(rename_all = "camelCase")]
5153pub struct ChaosExperiment {
5154 #[serde(skip_serializing_if = "Option::is_none")]
5156 pub name: Option<String>,
5157
5158 #[serde(rename = "loop", skip_serializing_if = "Option::is_none")]
5161 pub loop_back: Option<bool>,
5162
5163 pub stages: Vec<ChaosStage>,
5165}
5166
5167impl ChaosExperiment {
5168 pub fn new(stages: Vec<ChaosStage>) -> Self {
5170 Self {
5171 name: None,
5172 loop_back: None,
5173 stages,
5174 }
5175 }
5176
5177 pub fn name(mut self, name: impl Into<String>) -> Self {
5179 self.name = Some(name.into());
5180 self
5181 }
5182
5183 pub fn loop_back(mut self, loop_back: bool) -> Self {
5185 self.loop_back = Some(loop_back);
5186 self
5187 }
5188}
5189
5190#[cfg(test)]
5195mod tests {
5196 use super::*;
5197
5198 #[test]
5199 fn test_grpc_services_deserialize_from_server_wire_shape() {
5200 let wire = r#"[
5203 {
5204 "name": "helloworld.Greeter",
5205 "methods": [
5206 {
5207 "name": "SayHello",
5208 "inputType": "helloworld.HelloRequest",
5209 "outputType": "helloworld.HelloReply",
5210 "clientStreaming": false,
5211 "serverStreaming": false
5212 },
5213 {
5214 "name": "LotsOfReplies",
5215 "inputType": "helloworld.HelloRequest",
5216 "outputType": "helloworld.HelloReply",
5217 "clientStreaming": false,
5218 "serverStreaming": true
5219 }
5220 ]
5221 }
5222 ]"#;
5223
5224 let services: Vec<GrpcService> = serde_json::from_str(wire).unwrap();
5225 assert_eq!(services.len(), 1);
5226 let svc = &services[0];
5227 assert_eq!(svc.name, "helloworld.Greeter");
5228 assert_eq!(svc.methods.len(), 2);
5229
5230 let unary = &svc.methods[0];
5231 assert_eq!(unary.name, "SayHello");
5232 assert_eq!(unary.input_type, "helloworld.HelloRequest");
5233 assert_eq!(unary.output_type, "helloworld.HelloReply");
5234 assert!(!unary.client_streaming);
5235 assert!(!unary.server_streaming);
5236
5237 let server_stream = &svc.methods[1];
5238 assert_eq!(server_stream.name, "LotsOfReplies");
5239 assert!(!server_stream.client_streaming);
5240 assert!(server_stream.server_streaming);
5241 }
5242
5243 #[test]
5244 fn test_grpc_method_serializes_with_camel_case_keys() {
5245 let method = GrpcMethod {
5246 name: "BidiChat".into(),
5247 input_type: "chat.Message".into(),
5248 output_type: "chat.Message".into(),
5249 client_streaming: true,
5250 server_streaming: true,
5251 };
5252 let value = serde_json::to_value(&method).unwrap();
5253 assert_eq!(value["name"], "BidiChat");
5254 assert_eq!(value["inputType"], "chat.Message");
5255 assert_eq!(value["outputType"], "chat.Message");
5256 assert_eq!(value["clientStreaming"], true);
5257 assert_eq!(value["serverStreaming"], true);
5258 }
5259
5260 #[test]
5261 fn test_grpc_services_empty_array() {
5262 let services: Vec<GrpcService> = serde_json::from_str("[]").unwrap();
5263 assert!(services.is_empty());
5264 }
5265
5266 #[test]
5267 fn test_grpc_service_round_trips() {
5268 let original = GrpcService {
5269 name: "helloworld.Greeter".into(),
5270 methods: vec![GrpcMethod {
5271 name: "SayHello".into(),
5272 input_type: "helloworld.HelloRequest".into(),
5273 output_type: "helloworld.HelloReply".into(),
5274 client_streaming: false,
5275 server_streaming: false,
5276 }],
5277 };
5278 let json = serde_json::to_string(&original).unwrap();
5279 let parsed: GrpcService = serde_json::from_str(&json).unwrap();
5280 assert_eq!(original, parsed);
5281 }
5282}