spikard_core/
http.rs

1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4/// HTTP method
5#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
6pub enum Method {
7    Get,
8    Post,
9    Put,
10    Patch,
11    Delete,
12    Head,
13    Options,
14    Trace,
15}
16
17impl Method {
18    pub fn as_str(&self) -> &'static str {
19        match self {
20            Method::Get => "GET",
21            Method::Post => "POST",
22            Method::Put => "PUT",
23            Method::Patch => "PATCH",
24            Method::Delete => "DELETE",
25            Method::Head => "HEAD",
26            Method::Options => "OPTIONS",
27            Method::Trace => "TRACE",
28        }
29    }
30}
31
32impl std::fmt::Display for Method {
33    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34        write!(f, "{}", self.as_str())
35    }
36}
37
38impl std::str::FromStr for Method {
39    type Err = String;
40
41    fn from_str(s: &str) -> Result<Self, Self::Err> {
42        match s.to_uppercase().as_str() {
43            "GET" => Ok(Method::Get),
44            "POST" => Ok(Method::Post),
45            "PUT" => Ok(Method::Put),
46            "PATCH" => Ok(Method::Patch),
47            "DELETE" => Ok(Method::Delete),
48            "HEAD" => Ok(Method::Head),
49            "OPTIONS" => Ok(Method::Options),
50            "TRACE" => Ok(Method::Trace),
51            _ => Err(format!("Unknown HTTP method: {}", s)),
52        }
53    }
54}
55
56/// CORS configuration for a route
57#[derive(Debug, Clone, Serialize, Deserialize)]
58pub struct CorsConfig {
59    pub allowed_origins: Vec<String>,
60    pub allowed_methods: Vec<String>,
61    #[serde(default)]
62    pub allowed_headers: Vec<String>,
63    #[serde(skip_serializing_if = "Option::is_none")]
64    pub expose_headers: Option<Vec<String>>,
65    #[serde(skip_serializing_if = "Option::is_none")]
66    pub max_age: Option<u32>,
67    #[serde(skip_serializing_if = "Option::is_none")]
68    pub allow_credentials: Option<bool>,
69}
70
71/// Route metadata extracted from bindings
72#[derive(Debug, Clone, Serialize, Deserialize)]
73pub struct RouteMetadata {
74    pub method: String,
75    pub path: String,
76    pub handler_name: String,
77    pub request_schema: Option<Value>,
78    pub response_schema: Option<Value>,
79    pub parameter_schema: Option<Value>,
80    #[serde(skip_serializing_if = "Option::is_none")]
81    pub file_params: Option<Value>,
82    pub is_async: bool,
83    pub cors: Option<CorsConfig>,
84    /// Name of the body parameter (defaults to "body" if not specified)
85    #[serde(skip_serializing_if = "Option::is_none")]
86    pub body_param_name: Option<String>,
87    /// List of dependency keys this handler requires (for DI)
88    #[cfg(feature = "di")]
89    #[serde(skip_serializing_if = "Option::is_none")]
90    pub handler_dependencies: Option<Vec<String>>,
91}
92
93/// Compression configuration shared across runtimes
94#[derive(Debug, Clone, Serialize, Deserialize)]
95pub struct CompressionConfig {
96    /// Enable gzip compression
97    #[serde(default = "default_true")]
98    pub gzip: bool,
99    /// Enable brotli compression
100    #[serde(default = "default_true")]
101    pub brotli: bool,
102    /// Minimum response size to compress (bytes)
103    #[serde(default = "default_compression_min_size")]
104    pub min_size: usize,
105    /// Compression quality (0-11 for brotli, 0-9 for gzip)
106    #[serde(default = "default_compression_quality")]
107    pub quality: u32,
108}
109
110const fn default_true() -> bool {
111    true
112}
113
114const fn default_compression_min_size() -> usize {
115    1024
116}
117
118const fn default_compression_quality() -> u32 {
119    6
120}
121
122impl Default for CompressionConfig {
123    fn default() -> Self {
124        Self {
125            gzip: true,
126            brotli: true,
127            min_size: default_compression_min_size(),
128            quality: default_compression_quality(),
129        }
130    }
131}
132
133/// Rate limiting configuration shared across runtimes
134#[derive(Debug, Clone, Serialize, Deserialize)]
135pub struct RateLimitConfig {
136    /// Requests per second
137    pub per_second: u64,
138    /// Burst allowance
139    pub burst: u32,
140    /// Use IP-based rate limiting
141    #[serde(default = "default_true")]
142    pub ip_based: bool,
143}
144
145impl Default for RateLimitConfig {
146    fn default() -> Self {
147        Self {
148            per_second: 100,
149            burst: 200,
150            ip_based: true,
151        }
152    }
153}