1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
7#[serde(deny_unknown_fields)]
8pub struct AllowRule {
9 pub scheme: String,
10 pub host: String,
11 pub port: u16,
12 pub path_prefix: String,
13}
14
15#[derive(Debug, Clone, Copy, Default, Deserialize, Serialize, PartialEq, Eq)]
16pub enum CommandMode {
17 #[serde(rename = "read")]
18 Read,
19 #[default]
20 #[serde(rename = "write")]
21 Write,
22}
23
24impl CommandMode {
25 pub fn as_str(&self) -> &'static str {
26 match self {
27 CommandMode::Read => "read",
28 CommandMode::Write => "write",
29 }
30 }
31}
32
33#[derive(Debug, Clone, Deserialize, Serialize)]
34#[serde(deny_unknown_fields)]
35pub struct ParamSpec {
36 pub name: String,
37 #[serde(rename = "type")]
38 pub r#type: ParamType,
39 #[serde(default)]
40 pub required: bool,
41 pub default: Option<Value>,
42 pub description: Option<String>,
43}
44
45#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
46#[serde(rename_all = "lowercase")]
47pub enum ParamType {
48 String,
49 Integer,
50 Number,
51 Boolean,
52 Null,
53 Array,
54 Object,
55}
56
57impl std::fmt::Display for ParamType {
58 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59 let label = match self {
60 ParamType::String => "string",
61 ParamType::Integer => "integer",
62 ParamType::Number => "number",
63 ParamType::Boolean => "boolean",
64 ParamType::Null => "null",
65 ParamType::Array => "array",
66 ParamType::Object => "object",
67 };
68 write!(f, "{label}")
69 }
70}
71
72#[derive(Debug, Clone, Deserialize, Serialize)]
73#[serde(tag = "kind", rename_all = "snake_case")]
74pub enum AuthTemplate {
75 None,
76 ApiKey {
77 location: ApiKeyLocation,
78 name: String,
79 secret: String,
80 },
81 Bearer {
82 secret: String,
83 },
84 Basic {
85 username: String,
86 password_secret: String,
87 },
88 OAuth2Profile {
89 profile: String,
90 },
91}
92
93#[derive(Debug, Clone, Deserialize, Serialize)]
94#[serde(rename_all = "snake_case")]
95pub enum ApiKeyLocation {
96 Header,
97 Query,
98 Cookie,
99}
100
101#[derive(Debug, Clone, Deserialize, Serialize)]
102#[serde(tag = "kind", rename_all = "snake_case")]
103pub enum BodyTemplate {
104 None,
105 Json {
106 value: Value,
107 },
108 FormUrlencoded {
109 fields: BTreeMap<String, Value>,
110 },
111 Multipart {
112 parts: Vec<MultipartPartTemplate>,
113 },
114 RawText {
115 value: String,
116 content_type: Option<String>,
117 },
118 RawBytesBase64 {
119 value: String,
120 content_type: Option<String>,
121 },
122 FileStream {
123 path: String,
124 content_type: Option<String>,
125 },
126}
127
128#[derive(Debug, Clone, Deserialize, Serialize)]
129#[serde(deny_unknown_fields)]
130pub struct MultipartPartTemplate {
131 pub name: String,
132 pub value: Option<String>,
133 pub bytes_base64: Option<String>,
134 pub file_path: Option<String>,
135 pub content_type: Option<String>,
136 pub filename: Option<String>,
137}
138
139#[derive(Debug, Clone, Deserialize, Serialize)]
140#[serde(deny_unknown_fields)]
141pub struct TransportTemplate {
142 pub timeout_ms: Option<u64>,
143 pub max_response_bytes: Option<u64>,
144 pub redirects: Option<RedirectTemplate>,
145 pub retry: Option<RetryTemplate>,
146 pub compression: Option<bool>,
147 pub tls: Option<TlsTemplate>,
148 pub proxy_profile: Option<String>,
149}
150
151#[derive(Debug, Clone, Deserialize, Serialize)]
152#[serde(deny_unknown_fields)]
153pub struct RedirectTemplate {
154 #[serde(default = "default_follow_redirects")]
155 pub follow: bool,
156 #[serde(default = "default_redirect_hops")]
157 pub max_hops: usize,
158}
159
160#[derive(Debug, Clone, Deserialize, Serialize)]
161#[serde(deny_unknown_fields)]
162pub struct RetryTemplate {
163 #[serde(default)]
164 pub max_attempts: usize,
165 #[serde(default = "default_backoff_ms")]
166 pub backoff_ms: u64,
167 #[serde(default)]
168 pub retry_on_status: Vec<u16>,
169}
170
171#[derive(Debug, Clone, Deserialize, Serialize)]
172#[serde(deny_unknown_fields)]
173pub struct TlsTemplate {
174 pub min_version: Option<String>,
175}
176
177#[derive(Debug, Clone, Deserialize, Serialize)]
178#[serde(deny_unknown_fields)]
179pub struct ResultTemplate {
180 #[serde(default)]
181 pub decode: ResultDecode,
182 pub extract: Option<ResultExtract>,
183 #[serde(default = "default_result_output")]
184 pub output: String,
185 pub result_alias: Option<String>,
186}
187
188impl Default for ResultTemplate {
189 fn default() -> Self {
190 Self {
191 decode: ResultDecode::default(),
192 extract: None,
193 output: default_result_output(),
194 result_alias: None,
195 }
196 }
197}
198
199fn default_result_output() -> String {
200 "{{ result }}".to_string()
201}
202
203#[derive(Debug, Clone, Copy, Default, Deserialize, Serialize)]
204#[serde(rename_all = "snake_case")]
205pub enum ResultDecode {
206 #[default]
207 Auto,
208 Json,
209 Text,
210 Html,
211 Xml,
212 Binary,
213}
214
215#[derive(Debug, Clone, Deserialize, Serialize)]
216#[serde(untagged)]
217pub enum ResultExtract {
218 JsonPointer { json_pointer: String },
219 Regex { regex: String },
220 XPath { xpath: String },
221 CssSelector { css_selector: String },
222}
223
224pub fn default_follow_redirects() -> bool {
225 true
226}
227
228pub fn default_redirect_hops() -> usize {
229 5
230}
231
232pub fn default_backoff_ms() -> u64 {
233 250
234}