1use serde::{Deserialize, Serialize};
9use std::fmt;
10use std::str::FromStr;
11
12#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
14pub enum HttpMethod {
15 GET,
16 POST,
17 PUT,
18 DELETE,
19 HEAD,
20 OPTIONS,
21 PATCH,
22 CONNECT,
23 TRACE,
24 #[serde(untagged)]
25 Custom(String),
26}
27
28impl FromStr for HttpMethod {
29 type Err = std::convert::Infallible;
30
31 fn from_str(s: &str) -> Result<Self, Self::Err> {
32 Ok(match s.to_uppercase().as_str() {
33 "GET" => Self::GET,
34 "POST" => Self::POST,
35 "PUT" => Self::PUT,
36 "DELETE" => Self::DELETE,
37 "HEAD" => Self::HEAD,
38 "OPTIONS" => Self::OPTIONS,
39 "PATCH" => Self::PATCH,
40 "CONNECT" => Self::CONNECT,
41 "TRACE" => Self::TRACE,
42 other => Self::Custom(other.to_string()),
43 })
44 }
45}
46
47impl fmt::Display for HttpMethod {
48 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49 match self {
50 Self::GET => write!(f, "GET"),
51 Self::POST => write!(f, "POST"),
52 Self::PUT => write!(f, "PUT"),
53 Self::DELETE => write!(f, "DELETE"),
54 Self::HEAD => write!(f, "HEAD"),
55 Self::OPTIONS => write!(f, "OPTIONS"),
56 Self::PATCH => write!(f, "PATCH"),
57 Self::CONNECT => write!(f, "CONNECT"),
58 Self::TRACE => write!(f, "TRACE"),
59 Self::Custom(method) => write!(f, "{}", method),
60 }
61 }
62}
63
64#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
66pub enum TlsVersion {
67 #[serde(rename = "TLS1.2")]
68 Tls12,
69 #[serde(rename = "TLS1.3")]
70 Tls13,
71}
72
73impl fmt::Display for TlsVersion {
74 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75 match self {
76 Self::Tls12 => write!(f, "TLS1.2"),
77 Self::Tls13 => write!(f, "TLS1.3"),
78 }
79 }
80}
81
82#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
104#[serde(rename_all = "lowercase")]
105pub enum TraceIdFormat {
106 #[default]
108 TinyFlake,
109
110 Uuid,
112}
113
114impl TraceIdFormat {
115 pub fn from_str_loose(s: &str) -> Self {
117 match s.to_lowercase().as_str() {
118 "uuid" | "uuid4" | "uuidv4" => TraceIdFormat::Uuid,
119 _ => TraceIdFormat::TinyFlake, }
121 }
122}
123
124impl fmt::Display for TraceIdFormat {
125 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126 match self {
127 TraceIdFormat::TinyFlake => write!(f, "tinyflake"),
128 TraceIdFormat::Uuid => write!(f, "uuid"),
129 }
130 }
131}
132
133#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
135#[serde(rename_all = "snake_case")]
136pub enum LoadBalancingAlgorithm {
137 RoundRobin,
138 LeastConnections,
139 Random,
140 IpHash,
141 Weighted,
142 ConsistentHash,
143 PowerOfTwoChoices,
144 Adaptive,
145 LeastTokensQueued,
151 Maglev,
157 LocalityAware,
163 PeakEwma,
169 DeterministicSubset,
175 WeightedLeastConnections,
181 Sticky,
188}
189
190#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
192#[serde(rename_all = "snake_case")]
193pub enum HealthCheckType {
194 Http {
195 path: String,
196 expected_status: u16,
197 #[serde(skip_serializing_if = "Option::is_none")]
198 host: Option<String>,
199 },
200 Tcp,
201 Grpc {
202 service: String,
203 },
204 Inference {
210 endpoint: String,
212 #[serde(default, skip_serializing_if = "Vec::is_empty")]
214 expected_models: Vec<String>,
215 #[serde(default, skip_serializing_if = "Option::is_none")]
217 readiness: Option<Box<crate::inference::InferenceReadinessConfig>>,
218 },
219}
220
221#[derive(Debug, Clone, Serialize, Deserialize)]
223pub struct RetryPolicy {
224 pub max_attempts: u32,
225 pub timeout_ms: u64,
226 pub backoff_base_ms: u64,
227 pub backoff_max_ms: u64,
228 pub retryable_status_codes: Vec<u16>,
229}
230
231impl Default for RetryPolicy {
232 fn default() -> Self {
233 Self {
234 max_attempts: 3,
235 timeout_ms: 30000,
236 backoff_base_ms: 100,
237 backoff_max_ms: 10000,
238 retryable_status_codes: vec![502, 503, 504],
239 }
240 }
241}
242
243#[derive(Debug, Clone, Serialize, Deserialize)]
245pub struct CircuitBreakerConfig {
246 pub failure_threshold: u32,
247 pub success_threshold: u32,
248 pub timeout_seconds: u64,
249 pub half_open_max_requests: u32,
250}
251
252impl Default for CircuitBreakerConfig {
253 fn default() -> Self {
254 Self {
255 failure_threshold: 5,
256 success_threshold: 2,
257 timeout_seconds: 30,
258 half_open_max_requests: 1,
259 }
260 }
261}
262
263#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
265#[serde(rename_all = "snake_case")]
266pub enum CircuitBreakerState {
267 Closed,
268 Open,
269 HalfOpen,
270}
271
272#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
295#[serde(transparent)]
296pub struct Priority(pub i32);
297
298impl Priority {
299 pub const LOW: Self = Self(10);
301 pub const NORMAL: Self = Self(50);
304 pub const HIGH: Self = Self(100);
306 pub const CRITICAL: Self = Self(1000);
309
310 #[inline]
312 pub const fn as_i32(self) -> i32 {
313 self.0
314 }
315}
316
317impl Default for Priority {
318 fn default() -> Self {
319 Self::NORMAL
320 }
321}
322
323impl std::fmt::Display for Priority {
324 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
325 write!(f, "{}", self.0)
326 }
327}
328
329impl From<i32> for Priority {
330 fn from(value: i32) -> Self {
331 Self(value)
332 }
333}
334
335#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
337pub struct TimeWindow {
338 pub seconds: u64,
339}
340
341impl TimeWindow {
342 pub fn new(seconds: u64) -> Self {
343 Self { seconds }
344 }
345
346 pub fn as_duration(&self) -> std::time::Duration {
347 std::time::Duration::from_secs(self.seconds)
348 }
349}
350
351#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
353pub struct ByteSize(pub usize);
354
355impl ByteSize {
356 pub const KB: usize = 1024;
357 pub const MB: usize = 1024 * 1024;
358 pub const GB: usize = 1024 * 1024 * 1024;
359
360 pub fn from_kb(kb: usize) -> Self {
361 Self(kb * Self::KB)
362 }
363
364 pub fn from_mb(mb: usize) -> Self {
365 Self(mb * Self::MB)
366 }
367
368 pub fn from_gb(gb: usize) -> Self {
369 Self(gb * Self::GB)
370 }
371
372 pub fn as_bytes(&self) -> usize {
373 self.0
374 }
375}
376
377impl fmt::Display for ByteSize {
378 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
379 if self.0 >= Self::GB {
380 write!(f, "{:.2}GB", self.0 as f64 / Self::GB as f64)
381 } else if self.0 >= Self::MB {
382 write!(f, "{:.2}MB", self.0 as f64 / Self::MB as f64)
383 } else if self.0 >= Self::KB {
384 write!(f, "{:.2}KB", self.0 as f64 / Self::KB as f64)
385 } else {
386 write!(f, "{}B", self.0)
387 }
388 }
389}
390
391impl Serialize for ByteSize {
392 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
393 where
394 S: serde::Serializer,
395 {
396 serializer.serialize_str(&self.to_string())
397 }
398}
399
400impl<'de> Deserialize<'de> for ByteSize {
401 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
402 where
403 D: serde::Deserializer<'de>,
404 {
405 let s = String::deserialize(deserializer)?;
406 Self::from_str(&s).map_err(serde::de::Error::custom)
407 }
408}
409
410impl FromStr for ByteSize {
411 type Err = String;
412
413 fn from_str(s: &str) -> Result<Self, Self::Err> {
414 let s = s.trim();
415 if s.is_empty() {
416 return Err("Empty byte size string".to_string());
417 }
418
419 if let Ok(bytes) = s.parse::<usize>() {
421 return Ok(Self(bytes));
422 }
423
424 let (num_part, unit_part) = s
426 .chars()
427 .position(|c| c.is_alphabetic())
428 .map(|i| s.split_at(i))
429 .ok_or_else(|| format!("Invalid byte size format: {}", s))?;
430
431 let value: f64 = num_part
432 .trim()
433 .parse()
434 .map_err(|_| format!("Invalid number: {}", num_part))?;
435
436 let multiplier = match unit_part.to_uppercase().as_str() {
437 "B" => 1,
438 "KB" | "K" => Self::KB,
439 "MB" | "M" => Self::MB,
440 "GB" | "G" => Self::GB,
441 _ => return Err(format!("Invalid unit: {}", unit_part)),
442 };
443
444 Ok(Self((value * multiplier as f64) as usize))
445 }
446}
447
448#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
450pub struct ClientIp {
451 pub address: std::net::IpAddr,
452 #[serde(skip_serializing_if = "Option::is_none")]
453 pub forwarded_for: Option<Vec<std::net::IpAddr>>,
454}
455
456impl fmt::Display for ClientIp {
457 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
458 write!(f, "{}", self.address)
459 }
460}
461
462#[cfg(test)]
463mod tests {
464 use super::*;
465
466 #[test]
467 fn test_http_method_parsing() {
468 assert_eq!(HttpMethod::from_str("GET").unwrap(), HttpMethod::GET);
469 assert_eq!(HttpMethod::from_str("post").unwrap(), HttpMethod::POST);
470 assert_eq!(
471 HttpMethod::from_str("PROPFIND").unwrap(),
472 HttpMethod::Custom("PROPFIND".to_string())
473 );
474 }
475
476 #[test]
477 fn test_byte_size_parsing() {
478 assert_eq!(ByteSize::from_str("1024").unwrap().0, 1024);
479 assert_eq!(ByteSize::from_str("10KB").unwrap().0, 10 * 1024);
480 assert_eq!(
481 ByteSize::from_str("5.5MB").unwrap().0,
482 (5.5 * 1024.0 * 1024.0) as usize
483 );
484 assert_eq!(ByteSize::from_str("2GB").unwrap().0, 2 * 1024 * 1024 * 1024);
485 assert_eq!(ByteSize::from_str("100 B").unwrap().0, 100);
486 }
487
488 #[test]
489 fn test_byte_size_display() {
490 assert_eq!(ByteSize(512).to_string(), "512B");
491 assert_eq!(ByteSize(2048).to_string(), "2.00KB");
492 assert_eq!(ByteSize(1024 * 1024).to_string(), "1.00MB");
493 assert_eq!(ByteSize(1024 * 1024 * 1024).to_string(), "1.00GB");
494 }
495
496 #[test]
497 fn test_trace_id_format() {
498 assert_eq!(TraceIdFormat::from_str_loose("uuid"), TraceIdFormat::Uuid);
499 assert_eq!(
500 TraceIdFormat::from_str_loose("tinyflake"),
501 TraceIdFormat::TinyFlake
502 );
503 assert_eq!(
504 TraceIdFormat::from_str_loose("unknown"),
505 TraceIdFormat::TinyFlake
506 );
507 }
508}