starberry_core/http/
http_value.rs

1#![allow(non_snake_case)] 
2#![allow(non_camel_case_types)] 
3
4use std::{collections::HashMap, hash::Hash}; 
5
6#[derive(Debug, Clone)]  
7pub enum HttpVersion { 
8    Http09,
9    Http10,
10    Http11,
11    Http20,
12    Http30, 
13    Unknown, 
14} 
15
16impl HttpVersion { 
17    pub fn to_string(&self) -> String { 
18        match self { 
19            HttpVersion::Http09 => "HTTP/0.9".to_string(), 
20            HttpVersion::Http10 => "HTTP/1.0".to_string(), 
21            HttpVersion::Http11 => "HTTP/1.1".to_string(), 
22            HttpVersion::Http20 => "HTTP/2.0".to_string(), 
23            HttpVersion::Http30 => "HTTP/3.0".to_string(), 
24            _ => "UNKNOWN".to_string(), 
25        } 
26    } 
27
28    pub fn from_string(version: &str) -> Self { 
29        match version { 
30            "HTTP/0.9" => HttpVersion::Http09, 
31            "HTTP/1.0" => HttpVersion::Http10, 
32            "HTTP/1.1" => HttpVersion::Http11, 
33            "HTTP/2.0" => HttpVersion::Http20, 
34            "HTTP/3.0" => HttpVersion::Http30, 
35            _ => HttpVersion::Unknown,  
36        }  
37    }  
38} 
39
40impl std::fmt::Display for HttpVersion { 
41    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 
42        write!(f, "{}", self.to_string()) 
43    } 
44} 
45
46#[derive(Debug, Clone, PartialEq)] 
47pub enum HttpMethod { 
48    GET, 
49    POST, 
50    PUT, 
51    DELETE, 
52    HEAD, 
53    OPTIONS, 
54    PATCH, 
55    TRACE, 
56    CONNECT, 
57    UNKNOWN, 
58} 
59
60impl HttpMethod { 
61    pub fn to_string(&self) -> String { 
62        match self { 
63            HttpMethod::GET => "GET".to_string(), 
64            HttpMethod::POST => "POST".to_string(), 
65            HttpMethod::PUT => "PUT".to_string(), 
66            HttpMethod::DELETE => "DELETE".to_string(), 
67            HttpMethod::HEAD => "HEAD".to_string(), 
68            HttpMethod::OPTIONS => "OPTIONS".to_string(), 
69            HttpMethod::PATCH => "PATCH".to_string(), 
70            HttpMethod::TRACE => "TRACE".to_string(), 
71            HttpMethod::CONNECT => "CONNECT".to_string(), 
72            _ => "UNKNOWN".to_string(), 
73        } 
74    } 
75
76    pub fn from_string(method: &str) -> Self { 
77        match method { 
78            "GET" => HttpMethod::GET, 
79            "POST" => HttpMethod::POST, 
80            "PUT" => HttpMethod::PUT, 
81            "DELETE" => HttpMethod::DELETE, 
82            "HEAD" => HttpMethod::HEAD, 
83            "OPTIONS" => HttpMethod::OPTIONS, 
84            "PATCH" => HttpMethod::PATCH, 
85            "TRACE" => HttpMethod::TRACE, 
86            "CONNECT" => HttpMethod::CONNECT, 
87            _ => HttpMethod::UNKNOWN,  
88        }  
89    }  
90} 
91
92impl std::fmt::Display for HttpMethod { 
93    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 
94        write!(f, "{}", self.to_string()) 
95    } 
96} 
97
98impl Default for HttpMethod { 
99    fn default() -> Self { 
100        HttpMethod::GET  
101    } 
102} 
103
104impl PartialEq<&HttpMethod> for HttpMethod { 
105    fn eq(&self, other: &&HttpMethod) -> bool { 
106        self == *other 
107    } 
108} 
109
110impl PartialEq<HttpMethod> for &HttpMethod {
111    fn eq(&self, other: &HttpMethod) -> bool {
112        **self == *other
113    }
114} 
115
116/// Represents HTTP status codes.
117///
118/// This enum includes the most common HTTP status codes and provides methods
119/// to check status code categories, convert between numeric and string representations,
120/// and perform other common operations.
121#[derive(Debug, Clone, PartialEq, Eq, Hash)]
122pub enum StatusCode {
123    // 1xx - Informational
124    CONTINUE = 100,
125    SWITCHING_PROTOCOLS = 101,
126    PROCESSING = 102,
127    EARLY_HINTS = 103,
128
129    // 2xx - Success
130    OK = 200,
131    CREATED = 201,
132    ACCEPTED = 202,
133    NON_AUTHORITATIVE_INFORMATION = 203,
134    NO_CONTENT = 204,
135    RESET_CONTENT = 205,
136    PARTIAL_CONTENT = 206,
137    MULTI_STATUS = 207,
138    ALREADY_REPORTED = 208,
139    IM_USED = 226,
140
141    // 3xx - Redirection
142    MULTIPLE_CHOICES = 300,
143    MOVED_PERMANENTLY = 301,
144    FOUND = 302,
145    SEE_OTHER = 303,
146    NOT_MODIFIED = 304,
147    USE_PROXY = 305,
148    TEMPORARY_REDIRECT = 307,
149    PERMANENT_REDIRECT = 308,
150
151    // 4xx - Client Error
152    BAD_REQUEST = 400,
153    UNAUTHORIZED = 401,
154    PAYMENT_REQUIRED = 402,
155    FORBIDDEN = 403,
156    NOT_FOUND = 404,
157    METHOD_NOT_ALLOWED = 405,
158    NOT_ACCEPTABLE = 406,
159    PROXY_AUTHENTICATION_REQUIRED = 407,
160    REQUEST_TIMEOUT = 408,
161    CONFLICT = 409,
162    GONE = 410,
163    LENGTH_REQUIRED = 411,
164    PRECONDITION_FAILED = 412,
165    PAYLOAD_TOO_LARGE = 413,
166    URI_TOO_LONG = 414,
167    UNSUPPORTED_MEDIA_TYPE = 415,
168    RANGE_NOT_SATISFIABLE = 416,
169    EXPECTATION_FAILED = 417,
170    IM_A_TEAPOT = 418,
171    MISDIRECTED_REQUEST = 421,
172    UNPROCESSABLE_ENTITY = 422,
173    LOCKED = 423,
174    FAILED_DEPENDENCY = 424,
175    TOO_EARLY = 425,
176    UPGRADE_REQUIRED = 426,
177    PRECONDITION_REQUIRED = 428,
178    TOO_MANY_REQUESTS = 429,
179    REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
180    UNAVAILABLE_FOR_LEGAL_REASONS = 451,
181
182    // 5xx - Server Error
183    INTERNAL_SERVER_ERROR = 500,
184    NOT_IMPLEMENTED = 501,
185    BAD_GATEWAY = 502,
186    SERVICE_UNAVAILABLE = 503,
187    GATEWAY_TIMEOUT = 504,
188    HTTP_VERSION_NOT_SUPPORTED = 505,
189    VARIANT_ALSO_NEGOTIATES = 506,
190    INSUFFICIENT_STORAGE = 507,
191    LOOP_DETECTED = 508,
192    NOT_EXTENDED = 510,
193    NETWORK_AUTHENTICATION_REQUIRED = 511,
194
195    // Unknown status code
196    UNKNOWN = 0,
197}
198
199impl StatusCode {
200    /// Returns the numeric value of the status code.
201    ///
202    /// # Returns
203    ///
204    /// The u16 value of this status code.
205    ///
206    /// # Examples
207    ///
208    /// ```
209    /// let code = StatusCode::OK;
210    /// assert_eq!(code.as_u16(), 200);
211    /// ```
212    pub fn as_u16(&self) -> u16 {
213        self.clone() as u16
214    }
215
216    /// Returns a string representation of the status code.
217    ///
218    /// # Returns
219    ///
220    /// A string containing both the numeric code and its reason phrase.
221    ///
222    /// # Examples
223    ///
224    /// ```
225    /// let code = StatusCode::OK;
226    /// assert_eq!(code.to_string(), "200 OK");
227    /// ```
228    pub fn to_string(&self) -> String {
229        match self {
230            StatusCode::CONTINUE => "100 Continue",
231            StatusCode::SWITCHING_PROTOCOLS => "101 Switching Protocols",
232            StatusCode::PROCESSING => "102 Processing",
233            StatusCode::EARLY_HINTS => "103 Early Hints",
234            
235            StatusCode::OK => "200 OK",
236            StatusCode::CREATED => "201 Created",
237            StatusCode::ACCEPTED => "202 Accepted",
238            StatusCode::NON_AUTHORITATIVE_INFORMATION => "203 Non-Authoritative Information",
239            StatusCode::NO_CONTENT => "204 No Content",
240            StatusCode::RESET_CONTENT => "205 Reset Content",
241            StatusCode::PARTIAL_CONTENT => "206 Partial Content",
242            StatusCode::MULTI_STATUS => "207 Multi-Status",
243            StatusCode::ALREADY_REPORTED => "208 Already Reported",
244            StatusCode::IM_USED => "226 IM Used",
245            
246            StatusCode::MULTIPLE_CHOICES => "300 Multiple Choices",
247            StatusCode::MOVED_PERMANENTLY => "301 Moved Permanently",
248            StatusCode::FOUND => "302 Found",
249            StatusCode::SEE_OTHER => "303 See Other",
250            StatusCode::NOT_MODIFIED => "304 Not Modified",
251            StatusCode::USE_PROXY => "305 Use Proxy",
252            StatusCode::TEMPORARY_REDIRECT => "307 Temporary Redirect",
253            StatusCode::PERMANENT_REDIRECT => "308 Permanent Redirect",
254            
255            StatusCode::BAD_REQUEST => "400 Bad Request",
256            StatusCode::UNAUTHORIZED => "401 Unauthorized",
257            StatusCode::PAYMENT_REQUIRED => "402 Payment Required",
258            StatusCode::FORBIDDEN => "403 Forbidden",
259            StatusCode::NOT_FOUND => "404 Not Found",
260            StatusCode::METHOD_NOT_ALLOWED => "405 Method Not Allowed",
261            StatusCode::NOT_ACCEPTABLE => "406 Not Acceptable",
262            StatusCode::PROXY_AUTHENTICATION_REQUIRED => "407 Proxy Authentication Required",
263            StatusCode::REQUEST_TIMEOUT => "408 Request Timeout",
264            StatusCode::CONFLICT => "409 Conflict",
265            StatusCode::GONE => "410 Gone",
266            StatusCode::LENGTH_REQUIRED => "411 Length Required",
267            StatusCode::PRECONDITION_FAILED => "412 Precondition Failed",
268            StatusCode::PAYLOAD_TOO_LARGE => "413 Payload Too Large",
269            StatusCode::URI_TOO_LONG => "414 URI Too Long",
270            StatusCode::UNSUPPORTED_MEDIA_TYPE => "415 Unsupported Media Type",
271            StatusCode::RANGE_NOT_SATISFIABLE => "416 Range Not Satisfiable",
272            StatusCode::EXPECTATION_FAILED => "417 Expectation Failed",
273            StatusCode::IM_A_TEAPOT => "418 I'm a teapot",
274            StatusCode::MISDIRECTED_REQUEST => "421 Misdirected Request",
275            StatusCode::UNPROCESSABLE_ENTITY => "422 Unprocessable Entity",
276            StatusCode::LOCKED => "423 Locked",
277            StatusCode::FAILED_DEPENDENCY => "424 Failed Dependency",
278            StatusCode::TOO_EARLY => "425 Too Early",
279            StatusCode::UPGRADE_REQUIRED => "426 Upgrade Required",
280            StatusCode::PRECONDITION_REQUIRED => "428 Precondition Required",
281            StatusCode::TOO_MANY_REQUESTS => "429 Too Many Requests",
282            StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE => "431 Request Header Fields Too Large",
283            StatusCode::UNAVAILABLE_FOR_LEGAL_REASONS => "451 Unavailable For Legal Reasons",
284            
285            StatusCode::INTERNAL_SERVER_ERROR => "500 Internal Server Error",
286            StatusCode::NOT_IMPLEMENTED => "501 Not Implemented",
287            StatusCode::BAD_GATEWAY => "502 Bad Gateway",
288            StatusCode::SERVICE_UNAVAILABLE => "503 Service Unavailable",
289            StatusCode::GATEWAY_TIMEOUT => "504 Gateway Timeout",
290            StatusCode::HTTP_VERSION_NOT_SUPPORTED => "505 HTTP Version Not Supported",
291            StatusCode::VARIANT_ALSO_NEGOTIATES => "506 Variant Also Negotiates",
292            StatusCode::INSUFFICIENT_STORAGE => "507 Insufficient Storage",
293            StatusCode::LOOP_DETECTED => "508 Loop Detected",
294            StatusCode::NOT_EXTENDED => "510 Not Extended",
295            StatusCode::NETWORK_AUTHENTICATION_REQUIRED => "511 Network Authentication Required",
296            
297            StatusCode::UNKNOWN => "0 Unknown",
298        }.to_string()
299    }
300
301    /// Gets only the reason phrase part of the status code.
302    ///
303    /// # Returns
304    ///
305    /// The reason phrase part of the status code.
306    pub fn reason_phrase(&self) -> &'static str {
307        match self {
308            StatusCode::CONTINUE => "Continue",
309            StatusCode::SWITCHING_PROTOCOLS => "Switching Protocols",
310            StatusCode::PROCESSING => "Processing",
311            StatusCode::EARLY_HINTS => "Early Hints",
312            
313            StatusCode::OK => "OK",
314            StatusCode::CREATED => "Created",
315            StatusCode::ACCEPTED => "Accepted",
316            StatusCode::NON_AUTHORITATIVE_INFORMATION => "Non-Authoritative Information",
317            StatusCode::NO_CONTENT => "No Content",
318            StatusCode::RESET_CONTENT => "Reset Content",
319            StatusCode::PARTIAL_CONTENT => "Partial Content",
320            StatusCode::MULTI_STATUS => "Multi-Status",
321            StatusCode::ALREADY_REPORTED => "Already Reported",
322            StatusCode::IM_USED => "IM Used",
323            
324            StatusCode::MULTIPLE_CHOICES => "Multiple Choices",
325            StatusCode::MOVED_PERMANENTLY => "Moved Permanently",
326            StatusCode::FOUND => "Found",
327            StatusCode::SEE_OTHER => "See Other",
328            StatusCode::NOT_MODIFIED => "Not Modified",
329            StatusCode::USE_PROXY => "Use Proxy",
330            StatusCode::TEMPORARY_REDIRECT => "Temporary Redirect",
331            StatusCode::PERMANENT_REDIRECT => "Permanent Redirect",
332            
333            StatusCode::BAD_REQUEST => "Bad Request",
334            StatusCode::UNAUTHORIZED => "Unauthorized",
335            StatusCode::PAYMENT_REQUIRED => "Payment Required",
336            StatusCode::FORBIDDEN => "Forbidden",
337            StatusCode::NOT_FOUND => "Not Found",
338            StatusCode::METHOD_NOT_ALLOWED => "Method Not Allowed",
339            StatusCode::NOT_ACCEPTABLE => "Not Acceptable",
340            StatusCode::PROXY_AUTHENTICATION_REQUIRED => "Proxy Authentication Required",
341            StatusCode::REQUEST_TIMEOUT => "Request Timeout",
342            StatusCode::CONFLICT => "Conflict",
343            StatusCode::GONE => "Gone",
344            StatusCode::LENGTH_REQUIRED => "Length Required",
345            StatusCode::PRECONDITION_FAILED => "Precondition Failed",
346            StatusCode::PAYLOAD_TOO_LARGE => "Payload Too Large",
347            StatusCode::URI_TOO_LONG => "URI Too Long",
348            StatusCode::UNSUPPORTED_MEDIA_TYPE => "Unsupported Media Type",
349            StatusCode::RANGE_NOT_SATISFIABLE => "Range Not Satisfiable",
350            StatusCode::EXPECTATION_FAILED => "Expectation Failed",
351            StatusCode::IM_A_TEAPOT => "I'm a teapot",
352            StatusCode::MISDIRECTED_REQUEST => "Misdirected Request",
353            StatusCode::UNPROCESSABLE_ENTITY => "Unprocessable Entity",
354            StatusCode::LOCKED => "Locked",
355            StatusCode::FAILED_DEPENDENCY => "Failed Dependency",
356            StatusCode::TOO_EARLY => "Too Early",
357            StatusCode::UPGRADE_REQUIRED => "Upgrade Required",
358            StatusCode::PRECONDITION_REQUIRED => "Precondition Required",
359            StatusCode::TOO_MANY_REQUESTS => "Too Many Requests",
360            StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE => "Request Header Fields Too Large",
361            StatusCode::UNAVAILABLE_FOR_LEGAL_REASONS => "Unavailable For Legal Reasons",
362            
363            StatusCode::INTERNAL_SERVER_ERROR => "Internal Server Error",
364            StatusCode::NOT_IMPLEMENTED => "Not Implemented",
365            StatusCode::BAD_GATEWAY => "Bad Gateway",
366            StatusCode::SERVICE_UNAVAILABLE => "Service Unavailable",
367            StatusCode::GATEWAY_TIMEOUT => "Gateway Timeout",
368            StatusCode::HTTP_VERSION_NOT_SUPPORTED => "HTTP Version Not Supported",
369            StatusCode::VARIANT_ALSO_NEGOTIATES => "Variant Also Negotiates",
370            StatusCode::INSUFFICIENT_STORAGE => "Insufficient Storage",
371            StatusCode::LOOP_DETECTED => "Loop Detected",
372            StatusCode::NOT_EXTENDED => "Not Extended",
373            StatusCode::NETWORK_AUTHENTICATION_REQUIRED => "Network Authentication Required",
374            
375            StatusCode::UNKNOWN => "Unknown",
376        }
377    }
378
379    /// Creates a status code from a u16 value.
380    ///
381    /// # Arguments
382    ///
383    /// * `code` - The numeric status code.
384    ///
385    /// # Returns
386    ///
387    /// The corresponding StatusCode enum value, or UNKNOWN for unsupported codes.
388    pub fn from_u16(code: u16) -> Self {
389        match code {
390            100 => StatusCode::CONTINUE,
391            101 => StatusCode::SWITCHING_PROTOCOLS,
392            102 => StatusCode::PROCESSING,
393            103 => StatusCode::EARLY_HINTS,
394            
395            200 => StatusCode::OK,
396            201 => StatusCode::CREATED,
397            202 => StatusCode::ACCEPTED,
398            203 => StatusCode::NON_AUTHORITATIVE_INFORMATION,
399            204 => StatusCode::NO_CONTENT,
400            205 => StatusCode::RESET_CONTENT,
401            206 => StatusCode::PARTIAL_CONTENT,
402            207 => StatusCode::MULTI_STATUS,
403            208 => StatusCode::ALREADY_REPORTED,
404            226 => StatusCode::IM_USED,
405            
406            300 => StatusCode::MULTIPLE_CHOICES,
407            301 => StatusCode::MOVED_PERMANENTLY,
408            302 => StatusCode::FOUND,
409            303 => StatusCode::SEE_OTHER,
410            304 => StatusCode::NOT_MODIFIED,
411            305 => StatusCode::USE_PROXY,
412            307 => StatusCode::TEMPORARY_REDIRECT,
413            308 => StatusCode::PERMANENT_REDIRECT,
414            
415            400 => StatusCode::BAD_REQUEST,
416            401 => StatusCode::UNAUTHORIZED,
417            402 => StatusCode::PAYMENT_REQUIRED,
418            403 => StatusCode::FORBIDDEN,
419            404 => StatusCode::NOT_FOUND,
420            405 => StatusCode::METHOD_NOT_ALLOWED,
421            406 => StatusCode::NOT_ACCEPTABLE,
422            407 => StatusCode::PROXY_AUTHENTICATION_REQUIRED,
423            408 => StatusCode::REQUEST_TIMEOUT,
424            409 => StatusCode::CONFLICT,
425            410 => StatusCode::GONE,
426            411 => StatusCode::LENGTH_REQUIRED,
427            412 => StatusCode::PRECONDITION_FAILED,
428            413 => StatusCode::PAYLOAD_TOO_LARGE,
429            414 => StatusCode::URI_TOO_LONG,
430            415 => StatusCode::UNSUPPORTED_MEDIA_TYPE,
431            416 => StatusCode::RANGE_NOT_SATISFIABLE,
432            417 => StatusCode::EXPECTATION_FAILED,
433            418 => StatusCode::IM_A_TEAPOT,
434            421 => StatusCode::MISDIRECTED_REQUEST,
435            422 => StatusCode::UNPROCESSABLE_ENTITY,
436            423 => StatusCode::LOCKED,
437            424 => StatusCode::FAILED_DEPENDENCY,
438            425 => StatusCode::TOO_EARLY,
439            426 => StatusCode::UPGRADE_REQUIRED,
440            428 => StatusCode::PRECONDITION_REQUIRED,
441            429 => StatusCode::TOO_MANY_REQUESTS,
442            431 => StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE,
443            451 => StatusCode::UNAVAILABLE_FOR_LEGAL_REASONS,
444            
445            500 => StatusCode::INTERNAL_SERVER_ERROR,
446            501 => StatusCode::NOT_IMPLEMENTED,
447            502 => StatusCode::BAD_GATEWAY,
448            503 => StatusCode::SERVICE_UNAVAILABLE,
449            504 => StatusCode::GATEWAY_TIMEOUT,
450            505 => StatusCode::HTTP_VERSION_NOT_SUPPORTED,
451            506 => StatusCode::VARIANT_ALSO_NEGOTIATES,
452            507 => StatusCode::INSUFFICIENT_STORAGE,
453            508 => StatusCode::LOOP_DETECTED,
454            510 => StatusCode::NOT_EXTENDED,
455            511 => StatusCode::NETWORK_AUTHENTICATION_REQUIRED,
456            
457            _ => StatusCode::UNKNOWN,
458        }
459    }
460
461    /// Creates a status code from a string representation.
462    ///
463    /// # Arguments
464    ///
465    /// * `code` - The string representation of the status code.
466    ///
467    /// # Returns
468    ///
469    /// The corresponding StatusCode enum value, or UNKNOWN if not recognized.
470    pub fn from_string(code: &str) -> Self {
471        // Try parsing just the numeric part first
472        if let Ok(num) = code.split_whitespace().next().unwrap_or("").parse::<u16>() {
473            return StatusCode::from_u16(num);
474        }
475        
476        // If that fails, try matching the full string
477        match code {
478            "100 Continue" => StatusCode::CONTINUE,
479            "101 Switching Protocols" => StatusCode::SWITCHING_PROTOCOLS,
480            "102 Processing" => StatusCode::PROCESSING,
481            "103 Early Hints" => StatusCode::EARLY_HINTS,
482            
483            "200 OK" => StatusCode::OK,
484            "201 Created" => StatusCode::CREATED,
485            "202 Accepted" => StatusCode::ACCEPTED,
486            "203 Non-Authoritative Information" => StatusCode::NON_AUTHORITATIVE_INFORMATION,
487            "204 No Content" => StatusCode::NO_CONTENT,
488            "205 Reset Content" => StatusCode::RESET_CONTENT,
489            "206 Partial Content" => StatusCode::PARTIAL_CONTENT,
490            "207 Multi-Status" => StatusCode::MULTI_STATUS,
491            "208 Already Reported" => StatusCode::ALREADY_REPORTED,
492            "226 IM Used" => StatusCode::IM_USED,
493            
494            "300 Multiple Choices" => StatusCode::MULTIPLE_CHOICES,
495            "301 Moved Permanently" => StatusCode::MOVED_PERMANENTLY,
496            "302 Found" => StatusCode::FOUND,
497            "303 See Other" => StatusCode::SEE_OTHER,
498            "304 Not Modified" => StatusCode::NOT_MODIFIED,
499            "305 Use Proxy" => StatusCode::USE_PROXY,
500            "307 Temporary Redirect" => StatusCode::TEMPORARY_REDIRECT,
501            "308 Permanent Redirect" => StatusCode::PERMANENT_REDIRECT,
502            
503            "400 Bad Request" => StatusCode::BAD_REQUEST,
504            "401 Unauthorized" => StatusCode::UNAUTHORIZED,
505            "402 Payment Required" => StatusCode::PAYMENT_REQUIRED,
506            "403 Forbidden" => StatusCode::FORBIDDEN,
507            "404 Not Found" => StatusCode::NOT_FOUND,
508            "405 Method Not Allowed" => StatusCode::METHOD_NOT_ALLOWED,
509            "406 Not Acceptable" => StatusCode::NOT_ACCEPTABLE,
510            "407 Proxy Authentication Required" => StatusCode::PROXY_AUTHENTICATION_REQUIRED,
511            "408 Request Timeout" => StatusCode::REQUEST_TIMEOUT,
512            "409 Conflict" => StatusCode::CONFLICT,
513            "410 Gone" => StatusCode::GONE,
514            "411 Length Required" => StatusCode::LENGTH_REQUIRED,
515            "412 Precondition Failed" => StatusCode::PRECONDITION_FAILED,
516            "413 Payload Too Large" => StatusCode::PAYLOAD_TOO_LARGE,
517            "414 URI Too Long" => StatusCode::URI_TOO_LONG,
518            "415 Unsupported Media Type" => StatusCode::UNSUPPORTED_MEDIA_TYPE,
519            "416 Range Not Satisfiable" => StatusCode::RANGE_NOT_SATISFIABLE,
520            "417 Expectation Failed" => StatusCode::EXPECTATION_FAILED,
521            "418 I'm a teapot" => StatusCode::IM_A_TEAPOT,
522            "421 Misdirected Request" => StatusCode::MISDIRECTED_REQUEST,
523            "422 Unprocessable Entity" => StatusCode::UNPROCESSABLE_ENTITY,
524            "423 Locked" => StatusCode::LOCKED,
525            "424 Failed Dependency" => StatusCode::FAILED_DEPENDENCY,
526            "425 Too Early" => StatusCode::TOO_EARLY,
527            "426 Upgrade Required" => StatusCode::UPGRADE_REQUIRED,
528            "428 Precondition Required" => StatusCode::PRECONDITION_REQUIRED,
529            "429 Too Many Requests" => StatusCode::TOO_MANY_REQUESTS,
530            "431 Request Header Fields Too Large" => StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE,
531            "451 Unavailable For Legal Reasons" => StatusCode::UNAVAILABLE_FOR_LEGAL_REASONS,
532            
533            "500 Internal Server Error" => StatusCode::INTERNAL_SERVER_ERROR,
534            "501 Not Implemented" => StatusCode::NOT_IMPLEMENTED,
535            "502 Bad Gateway" => StatusCode::BAD_GATEWAY,
536            "503 Service Unavailable" => StatusCode::SERVICE_UNAVAILABLE,
537            "504 Gateway Timeout" => StatusCode::GATEWAY_TIMEOUT,
538            "505 HTTP Version Not Supported" => StatusCode::HTTP_VERSION_NOT_SUPPORTED,
539            "506 Variant Also Negotiates" => StatusCode::VARIANT_ALSO_NEGOTIATES,
540            "507 Insufficient Storage" => StatusCode::INSUFFICIENT_STORAGE,
541            "508 Loop Detected" => StatusCode::LOOP_DETECTED,
542            "510 Not Extended" => StatusCode::NOT_EXTENDED,
543            "511 Network Authentication Required" => StatusCode::NETWORK_AUTHENTICATION_REQUIRED,
544            
545            _ => StatusCode::UNKNOWN,
546        }
547    }
548
549    /// Checks if the status code is informational (1xx).
550    ///
551    /// # Returns
552    ///
553    /// `true` if the status code is in the 1xx range, `false` otherwise.
554    pub fn is_informational(&self) -> bool {
555        let code = self.as_u16();
556        (100..=199).contains(&code)
557    }
558
559    /// Checks if the status code indicates success (2xx).
560    ///
561    /// # Returns
562    ///
563    /// `true` if the status code is in the 2xx range, `false` otherwise.
564    pub fn is_success(&self) -> bool {
565        let code = self.as_u16();
566        (200..=299).contains(&code)
567    }
568
569    /// Checks if the status code indicates redirection (3xx).
570    ///
571    /// # Returns
572    ///
573    /// `true` if the status code is in the 3xx range, `false` otherwise.
574    pub fn is_redirection(&self) -> bool {
575        let code = self.as_u16();
576        (300..=399).contains(&code)
577    }
578
579    /// Checks if the status code indicates a client error (4xx).
580    ///
581    /// # Returns
582    ///
583    /// `true` if the status code is in the 4xx range, `false` otherwise.
584    pub fn is_client_error(&self) -> bool {
585        let code = self.as_u16();
586        (400..=499).contains(&code)
587    }
588
589    /// Checks if the status code indicates a server error (5xx).
590    ///
591    /// # Returns
592    ///
593    /// `true` if the status code is in the 5xx range, `false` otherwise.
594    pub fn is_server_error(&self) -> bool {
595        let code = self.as_u16();
596        (500..=599).contains(&code)
597    }
598
599    /// Checks if the status code indicates an error (4xx or 5xx).
600    ///
601    /// # Returns
602    ///
603    /// `true` if the status code is in the 4xx or 5xx range, `false` otherwise.
604    pub fn is_error(&self) -> bool {
605        self.is_client_error() || self.is_server_error()
606    }
607
608    /// Checks if the status code indicates that the resource was not found (404).
609    ///
610    /// # Returns
611    ///
612    /// `true` if the status code is 404, `false` otherwise.
613    pub fn is_not_found(&self) -> bool {
614        *self == StatusCode::NOT_FOUND
615    }
616
617    /// Checks if the status code indicates a successful response (200 OK).
618    ///
619    /// # Returns
620    ///
621    /// `true` if the status code is 200, `false` otherwise.
622    pub fn is_ok(&self) -> bool {
623        *self == StatusCode::OK
624    }
625
626    /// Checks if the status code indicates that the content should be omitted (204 No Content).
627    ///
628    /// # Returns
629    ///
630    /// `true` if the status code is 204, `false` otherwise.
631    pub fn is_no_content(&self) -> bool {
632        *self == StatusCode::NO_CONTENT
633    }
634
635    /// Checks if the status code indicates that the requested resource was created (201 Created).
636    ///
637    /// # Returns
638    ///
639    /// `true` if the status code is 201, `false` otherwise.
640    pub fn is_created(&self) -> bool {
641        *self == StatusCode::CREATED
642    }
643    
644    /// Checks if the status code indicates that the client is not authorized (401 Unauthorized).
645    ///
646    /// # Returns
647    ///
648    /// `true` if the status code is 401, `false` otherwise.
649    pub fn is_unauthorized(&self) -> bool {
650        *self == StatusCode::UNAUTHORIZED
651    }
652}
653
654impl std::fmt::Display for StatusCode {
655    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
656        write!(f, "{}", self.to_string())
657    }
658}
659
660impl From<u16> for StatusCode {
661    fn from(code: u16) -> Self {
662        StatusCode::from_u16(code)
663    }
664}
665
666impl From<&str> for StatusCode {
667    fn from(code: &str) -> Self {
668        StatusCode::from_string(code)
669    }
670}
671
672impl From<String> for StatusCode {
673    fn from(code: String) -> Self {
674        StatusCode::from_string(&code)
675    }
676}
677
678impl From<StatusCode> for u16 {
679    fn from(code: StatusCode) -> Self {
680        code.as_u16()
681    }
682}
683
684impl From<StatusCode> for String {
685    fn from(code: StatusCode) -> Self {
686        code.to_string()
687    }
688} 
689
690/// Represents the content type of an HTTP message. 
691/// This enum is used to parse and construct HTTP headers related to content type. 
692/// It includes well-known content types like text, application, image, audio, video, and multipart. 
693/// It also includes a generic Other variant for any other content types. 
694#[derive(Debug, Clone, PartialEq, Eq)]
695pub enum HttpContentType {
696    // Well-known content types
697    Text { subtype: String, charset: Option<String> },
698    Application { subtype: String, parameters: Option<Vec<(String, String)>> },
699    Image { subtype: String },
700    Audio { subtype: String },
701    Video { subtype: String },
702    Multipart { subtype: String, boundary: Option<String> },
703    Other { type_name: String, subtype: String, parameters: Option<Vec<(String, String)>> },
704} 
705
706impl HttpContentType {
707    /// Converts a string into an HttpContentType enum variant 
708    /// This function parses the content type string and extracts the main type, subtype, and any parameters.
709    /// It supports well-known content types like text, application, image, audio, video, and multipart. ]
710    /// 
711    /// # Examples 
712    /// 
713    /// ```rust 
714    /// use starberry_core::http::http_value::HttpContentType; 
715    /// let content_type = HttpContentType::from_str("text/html; charset=UTF-8"); 
716    /// assert_eq!(content_type, HttpContentType::Text { subtype: "html".to_string(), charset: Some("UTF-8".to_string()) }); 
717    /// ``` 
718    pub fn from_str(content_type: &str) -> Self {
719        let parts: Vec<&str> = content_type.split(';').collect();
720        let main_part = parts[0].trim();
721        let mut parameters = Vec::new();
722
723        for part in &parts[1..] {
724            let param_parts: Vec<&str> = part.split('=').collect();
725            if param_parts.len() == 2 {
726                parameters.push((param_parts[0].trim().to_string(), param_parts[1].trim().to_string()));
727            }
728        }
729
730        let (type_name, subtype) = if let Some(pos) = main_part.find('/') {
731            (&main_part[..pos], &main_part[pos + 1..])
732        } else {
733            ("unknown", "unknown")
734        };
735
736        match type_name {
737            "text" => Self::Text { 
738                subtype: subtype.to_string(), 
739                charset: Self::find_value_from_vec(&parameters, "charset"),
740            }, 
741            "application" => Self::Application { 
742                subtype: subtype.to_string(), 
743                parameters: Some(parameters) 
744            },
745            "image" => Self::Image { 
746                subtype: subtype.to_string() 
747            },
748            "audio" => Self::Audio { 
749                subtype: subtype.to_string() 
750            },
751            "video" => Self::Video { 
752                subtype: subtype.to_string() 
753            },
754            "multipart" => Self::Multipart { 
755                subtype: subtype.to_string(), 
756                boundary: Self::find_value_from_vec(&parameters, "boundary"),  
757            },
758            _ => Self::Other {
759                type_name: type_name.to_string(), 
760                subtype: subtype.to_string(), 
761                parameters: Some(parameters) 
762            },
763        } 
764    } 
765
766    /// Find value from Vec<(String, String)> 
767    /// # Examples 
768    /// ```rust 
769    /// use starberry_core::http::http_value::HttpContentType; 
770    /// let vec = vec![("key1".to_string(), "value1".to_string()), ("key2".to_string(), "value2".to_string())]; 
771    /// let value = HttpContentType::find_value_from_vec(&vec, "key1"); 
772    /// assert_eq!(value, Some("value1".to_string())); 
773    /// ``` 
774    pub fn find_value_from_vec(vec: &Vec<(String, String)>, key: &str) -> Option<String> { 
775        for (k, v) in vec { 
776            if k == key { 
777                return Some(v.clone()); 
778            } 
779        } 
780        None 
781    } 
782
783    /// Converts an HttpContentType enum variant into its string representation
784    pub fn to_string(&self) -> String {
785        match self {
786            HttpContentType::Text { subtype, .. } => format!("text/{}", subtype),
787            HttpContentType::Application { subtype, .. } => format!("application/{}", subtype),
788            HttpContentType::Image { subtype } => format!("image/{}", subtype),
789            HttpContentType::Audio { subtype } => format!("audio/{}", subtype),
790            HttpContentType::Video { subtype } => format!("video/{}", subtype),
791            HttpContentType::Multipart { subtype, .. } => format!("multipart/{}", subtype),
792            HttpContentType::Other { type_name, subtype, .. } => format!("{}/{}", type_name, subtype),
793        }
794    } 
795
796    pub fn TextHtml() -> Self { 
797        Self::Text { subtype: "html".to_string(), charset: Some("UTF-8".to_string()) } 
798    } 
799
800    pub fn TextPlain() -> Self { 
801        Self::Text { subtype: "plain".to_string(), charset: Some("UTF-8".to_string()) } 
802    } 
803
804    pub fn TextCss() -> Self {
805        Self::Text { 
806            subtype: "css".to_string(), 
807            charset: Some("UTF-8".to_string()) 
808        }
809    }
810
811    pub fn ApplicationJavascript() -> Self {
812        Self::Application { 
813            subtype: "javascript".to_string(), 
814            parameters: Some(vec![("charset".to_string(), "UTF-8".to_string())]) 
815        }
816    } 
817
818    pub fn ApplicationJson() -> Self { 
819        Self::Application { subtype: "json".to_string(), parameters: Some(vec![("charset".to_string(), "UTF-8".to_string())]) } 
820    } 
821
822    pub fn ApplicationUrlEncodedForm() -> Self { 
823        Self::Application { subtype: "x-www-form-urlencoded".to_string(), parameters: Some(vec![("charset".to_string(), "UTF-8".to_string())]) } 
824    }
825
826    pub fn ApplicationXml() -> Self { 
827        Self::Application { subtype: "xml".to_string(), parameters: Some(vec![("charset".to_string(), "UTF-8".to_string())]) } 
828    } 
829
830    pub fn ApplicationOctetStream() -> Self { 
831        Self::Application { subtype: "octet-stream".to_string(), parameters: Some(vec![("charset".to_string(), "UTF-8".to_string())]) } 
832    } 
833
834    pub fn ImagePng() -> Self {
835        Self::Image { subtype: "png".to_string() }
836    }
837
838    pub fn ImageJpeg() -> Self {
839        Self::Image { subtype: "jpeg".to_string() }
840    }
841
842    pub fn ImageGif() -> Self {
843        Self::Image { subtype: "gif".to_string() }
844    } 
845}
846
847impl std::fmt::Display for HttpContentType {
848    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
849        write!(f, "{}", self.to_string())
850    }
851} 
852
853impl Default for HttpContentType { 
854    fn default() -> Self {
855        Self::Other { 
856            type_name: "Unknown".to_string(), 
857            subtype: "Unknown".to_string(), 
858            parameters: None, 
859        }
860    }
861}
862
863pub struct HeaderConstructor{ 
864    pub headers: Vec<HeaderAttribute>
865} 
866
867impl HeaderConstructor{ 
868    pub fn build<T: Into<String>>(string: T) -> Self { 
869        let mut headers = Vec::new(); 
870        let string = string.into(); 
871        let parts: Vec<&str> = string.split(';').collect(); 
872        for part in parts { 
873            let part = part.trim(); 
874            if !part.is_empty() { 
875                headers.push(HeaderAttribute::build(part)); 
876            } 
877        } 
878        Self { headers } 
879    }
880}
881
882pub struct HeaderAttribute{ 
883    pub main_value: String, 
884    pub attributes: HashMap<String, String>, 
885} 
886
887impl HeaderAttribute{ 
888    pub fn build<T: Into<String>>(part: T) -> Self{ 
889        let part = part.into(); 
890        let mut attributes = HashMap::new(); 
891        let main_value = part.split(':').next().unwrap_or("").trim().to_string(); 
892        for attr in part.split(';').skip(1) { 
893            let attr_parts: Vec<&str> = attr.split('=').collect(); 
894            if attr_parts.len() == 2 { 
895                attributes.insert(attr_parts[0].trim().to_string(), attr_parts[1].trim().to_string()); 
896            } 
897        } 
898        Self { main_value, attributes } 
899    }
900}  
901
902#[derive(Debug, Clone)] 
903pub struct RequestPath{ 
904    path: Vec<String> 
905} 
906
907impl RequestPath{   
908    pub fn new(path: Vec<String>) -> Self{ 
909        Self { path }  
910    } 
911
912    pub fn to_string(&self) -> String{ 
913        let mut result = String::new(); 
914        for part in &self.path { 
915            result.push('/'); 
916            result.push_str(part); 
917        } 
918        result 
919    } 
920
921    pub fn from_string(url: &str) -> Self{ 
922        let mut path = Vec::new(); 
923        let parts: Vec<&str> = url.split('/').collect(); 
924        for part in parts { 
925            if !part.is_empty() { 
926                path.push(part.to_string()); 
927            } 
928        } 
929        Self { path } 
930    } 
931
932    pub fn url_part(&self, part: usize) -> String{ 
933        // if part < 0 { 
934        //     return self.path[self.path.len() as usize + part as usize].clone(); 
935        // } else if part >= self.path.len() { 
936        //     return "".to_string(); 
937        // } 
938        // self.path[part].clone()  
939        if part >= self.path.len() { 
940            return "".to_string(); 
941        } 
942        self.path[part].clone()    
943    }
944} 
945
946impl Default for RequestPath {
947    fn default() -> Self {
948        Self::new(Vec::new())
949    }
950} 
951
952/// Represents HTTP `Accept-Language` header for client language preferences.
953/// 
954/// Stores language tags with quality weights (q-values) for content negotiation.
955/// 
956/// # RFC 7231 Compliance:
957/// - Language tags are case-insensitive (but stored in original case)
958/// - Default weight = 1.0 if not specified
959/// - Weights range 0.0-1.0 (higher = more preferred)
960/// - Order indicates priority for equal weights 
961#[derive(Debug, Clone, PartialEq)] 
962pub struct AcceptLang {
963    langs: Vec<(String, f32)>,
964}
965
966impl AcceptLang {
967    /// Parses an `Accept-Language` header string
968    /// 
969    /// # Example:
970    /// ```
971    /// let accept_lang = AcceptLang::from_str("en-US, fr;q=0.7, zh-CN;q=0.5");
972    /// ```
973    pub fn from_str<S: AsRef<str>>(s: S) -> Self {
974        let mut langs = Vec::new();
975        
976        for lang_str in s.as_ref().split(',') {
977            let mut parts = lang_str.splitn(2, ';');
978            let lang = parts.next().unwrap().trim().to_string();
979            
980            // Default weight = 1.0
981            let mut weight = 1.0;
982            
983            // Parse q-value if exists
984            if let Some(q_part) = parts.next() {
985                if let Some(q_str) = q_part.trim().strip_prefix("q=") {
986                    weight = q_str.trim().parse().unwrap_or(1.0);
987                }
988            } 
989            
990            langs.push((lang, weight));
991        }
992        
993        AcceptLang { langs }
994    }
995
996    /// Returns most preferred language (highest weight, original case)
997    /// 
998    /// # Defaults to "en" if:
999    /// - No languages exist
1000    /// - All weights <= 0.0
1001    /// 
1002    /// # Example:
1003    /// ```
1004    /// let lang = accept_lang.most_preferred(); // "en-US"
1005    /// ```
1006    pub fn most_preferred(&self) -> String {
1007        self.langs.iter()
1008            .max_by(|(_, w1), (_, w2)| w1.total_cmp(w2))
1009            .map(|(lang, _)| lang.clone())
1010            .unwrap_or_else(|| "en".to_string())
1011    } 
1012
1013    /// Returns all languages in original order
1014    pub fn all_languages(&self) -> Vec<String> {
1015        self.langs.iter().map(|(lang, _)| lang.clone()).collect()
1016    }
1017
1018    /// Gets weight for a language (case-insensitive)
1019    /// 
1020    /// # Returns 0.0 if not found
1021    pub fn get_weight(&self, lang: &str) -> f32 {
1022        self.langs.iter()
1023            .find(|(l, _)| l.eq_ignore_ascii_case(lang))
1024            .map(|(_, w)| *w)
1025            .unwrap_or(0.0)
1026    }
1027
1028    /// Adds language (maintains insertion order)
1029    pub fn add_language(&mut self, lang: String, weight: f32) {
1030        self.langs.push((lang, weight));
1031    }
1032
1033    /// Removes language (case-insensitive)
1034    pub fn remove_language(&mut self, lang: &str) {
1035        self.langs.retain(|(l, _)| !l.eq_ignore_ascii_case(lang));
1036    }
1037
1038    /// Serializes to `Accept-Language` header format
1039    /// 
1040    /// # Formatting rules:
1041    /// - Omits q-value for 1.0 weights
1042    /// - Trims trailing zeros (0.7 → "0.7", 0.500 → "0.5")
1043    /// - Maintains original case
1044    pub fn to_header_string(&self) -> String {
1045        self.langs.iter()
1046            .map(|(lang, weight)| {
1047                if (weight - 1.0).abs() < f32::EPSILON {
1048                    lang.clone()
1049                } else {
1050                    let weight_str = format!("{:.3}", weight)
1051                        .trim_end_matches('0')
1052                        .trim_end_matches('.')
1053                        .to_string();
1054                    format!("{};q={}", lang, weight_str)
1055                }
1056            })
1057            .collect::<Vec<_>>()
1058            .join(", ")
1059    } 
1060
1061    pub fn to_response_header(&self) -> String {
1062        self.most_preferred() 
1063    }  
1064}