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
6use once_cell::sync::Lazy;
7
8#[derive(Debug, Clone)]  
9pub enum HttpVersion { 
10    Http09,
11    Http10,
12    Http11,
13    Http20,
14    Http30, 
15    Unknown, 
16} 
17
18impl HttpVersion { 
19    pub fn to_string(&self) -> String { 
20        match self { 
21            HttpVersion::Http09 => "HTTP/0.9".to_string(), 
22            HttpVersion::Http10 => "HTTP/1.0".to_string(), 
23            HttpVersion::Http11 => "HTTP/1.1".to_string(), 
24            HttpVersion::Http20 => "HTTP/2.0".to_string(), 
25            HttpVersion::Http30 => "HTTP/3.0".to_string(), 
26            _ => "UNKNOWN".to_string(), 
27        } 
28    } 
29
30    pub fn from_string(version: &str) -> Self { 
31        match version { 
32            "HTTP/0.9" => HttpVersion::Http09, 
33            "HTTP/1.0" => HttpVersion::Http10, 
34            "HTTP/1.1" => HttpVersion::Http11, 
35            "HTTP/2.0" => HttpVersion::Http20, 
36            "HTTP/3.0" => HttpVersion::Http30, 
37            _ => HttpVersion::Unknown,  
38        }  
39    }  
40} 
41
42impl std::fmt::Display for HttpVersion { 
43    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 
44        write!(f, "{}", self.to_string()) 
45    } 
46} 
47
48#[derive(Debug, Clone, PartialEq)] 
49pub enum HttpMethod { 
50    GET, 
51    POST, 
52    PUT, 
53    DELETE, 
54    HEAD, 
55    OPTIONS, 
56    PATCH, 
57    TRACE, 
58    CONNECT, 
59    UNKNOWN, 
60} 
61
62impl HttpMethod { 
63    pub fn to_string(&self) -> String { 
64        match self { 
65            HttpMethod::GET => "GET".to_string(), 
66            HttpMethod::POST => "POST".to_string(), 
67            HttpMethod::PUT => "PUT".to_string(), 
68            HttpMethod::DELETE => "DELETE".to_string(), 
69            HttpMethod::HEAD => "HEAD".to_string(), 
70            HttpMethod::OPTIONS => "OPTIONS".to_string(), 
71            HttpMethod::PATCH => "PATCH".to_string(), 
72            HttpMethod::TRACE => "TRACE".to_string(), 
73            HttpMethod::CONNECT => "CONNECT".to_string(), 
74            _ => "UNKNOWN".to_string(), 
75        } 
76    } 
77
78    pub fn from_string(method: &str) -> Self { 
79        match method { 
80            "GET" => HttpMethod::GET, 
81            "POST" => HttpMethod::POST, 
82            "PUT" => HttpMethod::PUT, 
83            "DELETE" => HttpMethod::DELETE, 
84            "HEAD" => HttpMethod::HEAD, 
85            "OPTIONS" => HttpMethod::OPTIONS, 
86            "PATCH" => HttpMethod::PATCH, 
87            "TRACE" => HttpMethod::TRACE, 
88            "CONNECT" => HttpMethod::CONNECT, 
89            _ => HttpMethod::UNKNOWN,  
90        }  
91    }  
92} 
93
94impl std::fmt::Display for HttpMethod { 
95    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 
96        write!(f, "{}", self.to_string()) 
97    } 
98} 
99
100impl PartialEq<&HttpMethod> for HttpMethod { 
101    fn eq(&self, other: &&HttpMethod) -> bool { 
102        self == *other 
103    } 
104} 
105
106impl PartialEq<HttpMethod> for &HttpMethod {
107    fn eq(&self, other: &HttpMethod) -> bool {
108        **self == *other
109    }
110} 
111
112pub enum StatusCode { 
113    OK = 200, 
114    CREATED = 201, 
115    ACCEPTED = 202, 
116    NO_CONTENT = 204, 
117    MOVED_PERMANENTLY = 301, 
118    FOUND = 302, 
119    NOT_MODIFIED = 304, 
120    BAD_REQUEST = 400, 
121    UNAUTHORIZED = 401, 
122    FORBIDDEN = 403, 
123    NOT_FOUND = 404, 
124    METHOD_NOT_ALLOWED = 405, 
125    UNSUPPORTED_MEDIA_TYPE = 415, 
126    INTERNAL_SERVER_ERROR = 500, 
127    NOT_IMPLEMENTED = 501, 
128    BAD_GATEWAY = 502, 
129    SERVICE_UNAVAILABLE = 503,  
130    GATEWAY_TIMEOUT = 504, 
131} 
132
133impl StatusCode { 
134    pub fn to_string(&self) -> String { 
135        match self { 
136            StatusCode::OK => "200 OK".to_string(), 
137            StatusCode::CREATED => "201 Created".to_string(), 
138            StatusCode::ACCEPTED => "202 Accepted".to_string(), 
139            StatusCode::NO_CONTENT => "204 No Content".to_string(), 
140            StatusCode::MOVED_PERMANENTLY => "301 Moved Permanently".to_string(), 
141            StatusCode::FOUND => "302 Found".to_string(), 
142            StatusCode::NOT_MODIFIED => "304 Not Modified".to_string(), 
143            StatusCode::BAD_REQUEST => "400 Bad Request".to_string(), 
144            StatusCode::UNAUTHORIZED => "401 Unauthorized".to_string(), 
145            StatusCode::FORBIDDEN => "403 Forbidden".to_string(), 
146            StatusCode::NOT_FOUND => "404 Not Found".to_string(), 
147            StatusCode::METHOD_NOT_ALLOWED => "405 Method Not Allowed".to_string(), 
148            StatusCode::UNSUPPORTED_MEDIA_TYPE => "415 Unsupported Media Type".to_string(), 
149            StatusCode::INTERNAL_SERVER_ERROR => "500 Internal Server Error".to_string(), 
150            StatusCode::NOT_IMPLEMENTED => "501 Not Implemented".to_string(), 
151            StatusCode::BAD_GATEWAY => "502 Bad Gateway".to_string(), 
152            StatusCode::SERVICE_UNAVAILABLE => "503 Service Unavailable".to_string(), 
153            StatusCode::GATEWAY_TIMEOUT => "504 Gateway Timeout".to_string(),  
154        } 
155    } 
156
157    pub fn to_u16(&self) -> u16 { 
158        match self { 
159            StatusCode::OK => 200, 
160            StatusCode::CREATED => 201, 
161            StatusCode::ACCEPTED => 202, 
162            StatusCode::NO_CONTENT => 204, 
163            StatusCode::MOVED_PERMANENTLY => 301, 
164            StatusCode::FOUND => 302, 
165            StatusCode::NOT_MODIFIED => 304, 
166            StatusCode::BAD_REQUEST => 400, 
167            StatusCode::UNAUTHORIZED => 401, 
168            StatusCode::FORBIDDEN => 403, 
169            StatusCode::NOT_FOUND => 404, 
170            StatusCode::METHOD_NOT_ALLOWED => 405, 
171            StatusCode::UNSUPPORTED_MEDIA_TYPE => 415, 
172            StatusCode::INTERNAL_SERVER_ERROR => 500, 
173            StatusCode::NOT_IMPLEMENTED => 501, 
174            StatusCode::BAD_GATEWAY => 502, 
175            StatusCode::SERVICE_UNAVAILABLE => 503,  
176            StatusCode::GATEWAY_TIMEOUT => 504,  
177        } 
178    } 
179
180    pub fn from_u16(code: u16) -> Self { 
181        match code { 
182            200 => StatusCode::OK, 
183            201 => StatusCode::CREATED, 
184            202 => StatusCode::ACCEPTED, 
185            204 => StatusCode::NO_CONTENT, 
186            301 => StatusCode::MOVED_PERMANENTLY, 
187            302 => StatusCode::FOUND, 
188            304 => StatusCode::NOT_MODIFIED, 
189            400 => StatusCode::BAD_REQUEST, 
190            401 => StatusCode::UNAUTHORIZED, 
191            403 => StatusCode::FORBIDDEN, 
192            404 => StatusCode::NOT_FOUND, 
193            405 => StatusCode::METHOD_NOT_ALLOWED, 
194            415 => StatusCode::UNSUPPORTED_MEDIA_TYPE, 
195            500 => StatusCode::INTERNAL_SERVER_ERROR, 
196            501 => StatusCode::NOT_IMPLEMENTED, 
197            502 => StatusCode::BAD_GATEWAY, 
198            503 => StatusCode::SERVICE_UNAVAILABLE,  
199            _ => StatusCode::INTERNAL_SERVER_ERROR, 
200        } 
201    } 
202
203    pub fn from_string(code: &str) -> Self { 
204        match code { 
205            "200 OK" => StatusCode::OK, 
206            "201 Created" => StatusCode::CREATED, 
207            "202 Accepted" => StatusCode::ACCEPTED, 
208            "204 No Content" => StatusCode::NO_CONTENT, 
209            "301 Moved Permanently" => StatusCode::MOVED_PERMANENTLY, 
210            "302 Found" => StatusCode::FOUND, 
211            "304 Not Modified" => StatusCode::NOT_MODIFIED, 
212            "400 Bad Request" => StatusCode::BAD_REQUEST, 
213            "401 Unauthorized" => StatusCode::UNAUTHORIZED, 
214            "403 Forbidden" => StatusCode::FORBIDDEN, 
215            "404 Not Found" => StatusCode::NOT_FOUND, 
216            "405 Method Not Allowed" => StatusCode::METHOD_NOT_ALLOWED, 
217            "415 Unsupported Media Type" => StatusCode::UNSUPPORTED_MEDIA_TYPE, 
218            "500 Internal Server Error" => StatusCode::INTERNAL_SERVER_ERROR, 
219            "501 Not Implemented" => StatusCode::NOT_IMPLEMENTED, 
220            "502 Bad Gateway" => StatusCode::BAD_GATEWAY, 
221            "503 Service Unavailable" => StatusCode::SERVICE_UNAVAILABLE,  
222            _ => StatusCode::INTERNAL_SERVER_ERROR, 
223        } 
224    } 
225} 
226
227/// Represents the content type of an HTTP message. 
228/// This enum is used to parse and construct HTTP headers related to content type. 
229/// It includes well-known content types like text, application, image, audio, video, and multipart. 
230/// It also includes a generic Other variant for any other content types. 
231#[derive(Debug, Clone, PartialEq, Eq)]
232pub enum HttpContentType {
233    // Well-known content types
234    Text { subtype: String, charset: Option<String> },
235    Application { subtype: String, parameters: Option<Vec<(String, String)>> },
236    Image { subtype: String },
237    Audio { subtype: String },
238    Video { subtype: String },
239    Multipart { subtype: String, boundary: Option<String> },
240    Other { type_name: String, subtype: String, parameters: Option<Vec<(String, String)>> },
241} 
242
243impl HttpContentType {
244    /// Converts a string into an HttpContentType enum variant 
245    /// This function parses the content type string and extracts the main type, subtype, and any parameters.
246    /// It supports well-known content types like text, application, image, audio, video, and multipart. ]
247    /// 
248    /// # Examples 
249    /// 
250    /// ```rust 
251    /// use starberry_core::http::http_value::HttpContentType; 
252    /// let content_type = HttpContentType::from_str("text/html; charset=UTF-8"); 
253    /// assert_eq!(content_type, HttpContentType::Text { subtype: "html".to_string(), charset: Some("UTF-8".to_string()) }); 
254    /// ``` 
255    pub fn from_str(content_type: &str) -> Self {
256        let parts: Vec<&str> = content_type.split(';').collect();
257        let main_part = parts[0].trim();
258        let mut parameters = Vec::new();
259
260        for part in &parts[1..] {
261            let param_parts: Vec<&str> = part.split('=').collect();
262            if param_parts.len() == 2 {
263                parameters.push((param_parts[0].trim().to_string(), param_parts[1].trim().to_string()));
264            }
265        }
266
267        let (type_name, subtype) = if let Some(pos) = main_part.find('/') {
268            (&main_part[..pos], &main_part[pos + 1..])
269        } else {
270            ("unknown", "unknown")
271        };
272
273        match type_name {
274            "text" => Self::Text { 
275                subtype: subtype.to_string(), 
276                charset: Self::find_value_from_vec(&parameters, "charset"),
277            }, 
278            "application" => Self::Application { 
279                subtype: subtype.to_string(), 
280                parameters: Some(parameters) 
281            },
282            "image" => Self::Image { 
283                subtype: subtype.to_string() 
284            },
285            "audio" => Self::Audio { 
286                subtype: subtype.to_string() 
287            },
288            "video" => Self::Video { 
289                subtype: subtype.to_string() 
290            },
291            "multipart" => Self::Multipart { 
292                subtype: subtype.to_string(), 
293                boundary: Self::find_value_from_vec(&parameters, "boundary"),  
294            },
295            _ => Self::Other {
296                type_name: type_name.to_string(), 
297                subtype: subtype.to_string(), 
298                parameters: Some(parameters) 
299            },
300        } 
301    } 
302
303    /// Find value from Vec<(String, String)> 
304    /// # Examples 
305    /// ```rust 
306    /// use starberry_core::http::http_value::HttpContentType; 
307    /// let vec = vec![("key1".to_string(), "value1".to_string()), ("key2".to_string(), "value2".to_string())]; 
308    /// let value = HttpContentType::find_value_from_vec(&vec, "key1"); 
309    /// assert_eq!(value, Some("value1".to_string())); 
310    /// ``` 
311    pub fn find_value_from_vec(vec: &Vec<(String, String)>, key: &str) -> Option<String> { 
312        for (k, v) in vec { 
313            if k == key { 
314                return Some(v.clone()); 
315            } 
316        } 
317        None 
318    } 
319
320    /// Converts an HttpContentType enum variant into its string representation
321    pub fn to_string(&self) -> String {
322        match self {
323            HttpContentType::Text { subtype, .. } => format!("text/{}", subtype),
324            HttpContentType::Application { subtype, .. } => format!("application/{}", subtype),
325            HttpContentType::Image { subtype } => format!("image/{}", subtype),
326            HttpContentType::Audio { subtype } => format!("audio/{}", subtype),
327            HttpContentType::Video { subtype } => format!("video/{}", subtype),
328            HttpContentType::Multipart { subtype, .. } => format!("multipart/{}", subtype),
329            HttpContentType::Other { type_name, subtype, .. } => format!("{}/{}", type_name, subtype),
330        }
331    } 
332
333    pub fn TextHtml() -> Self { 
334        Self::Text { subtype: "html".to_string(), charset: Some("UTF-8".to_string()) } 
335    } 
336
337    pub fn TextPlain() -> Self { 
338        Self::Text { subtype: "plain".to_string(), charset: Some("UTF-8".to_string()) } 
339    } 
340
341    pub fn ApplicationJson() -> Self { 
342        Self::Application { subtype: "json".to_string(), parameters: Some(vec![("charset".to_string(), "UTF-8".to_string())]) } 
343    } 
344
345    pub fn ApplicationXml() -> Self { 
346        Self::Application { subtype: "xml".to_string(), parameters: Some(vec![("charset".to_string(), "UTF-8".to_string())]) } 
347    } 
348
349    pub fn ApplicationOctetStream() -> Self { 
350        Self::Application { subtype: "octet-stream".to_string(), parameters: Some(vec![("charset".to_string(), "UTF-8".to_string())]) } 
351    } 
352}
353
354impl std::fmt::Display for HttpContentType {
355    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
356        write!(f, "{}", self.to_string())
357    }
358} 
359
360pub struct HeaderConstructor{ 
361    pub headers: Vec<HeaderAttribute>
362} 
363
364impl HeaderConstructor{ 
365    pub fn build<T: Into<String>>(string: T) -> Self { 
366        let mut headers = Vec::new(); 
367        let string = string.into(); 
368        let parts: Vec<&str> = string.split(';').collect(); 
369        for part in parts { 
370            let part = part.trim(); 
371            if !part.is_empty() { 
372                headers.push(HeaderAttribute::build(part)); 
373            } 
374        } 
375        Self { headers } 
376    }
377}
378
379pub struct HeaderAttribute{ 
380    pub main_value: String, 
381    pub attributes: HashMap<String, String>, 
382} 
383
384impl HeaderAttribute{ 
385    pub fn build<T: Into<String>>(part: T) -> Self{ 
386        let part = part.into(); 
387        let mut attributes = HashMap::new(); 
388        let main_value = part.split(':').next().unwrap_or("").trim().to_string(); 
389        for attr in part.split(';').skip(1) { 
390            let attr_parts: Vec<&str> = attr.split('=').collect(); 
391            if attr_parts.len() == 2 { 
392                attributes.insert(attr_parts[0].trim().to_string(), attr_parts[1].trim().to_string()); 
393            } 
394        } 
395        Self { main_value, attributes } 
396    }
397} 
398
399#[derive(Debug, Clone)] 
400pub struct UrlEncodedForm{ 
401    pub data: HashMap<String, String>  
402} 
403
404impl UrlEncodedForm{ 
405    /// Creates a new UrlEncodedForm with an empty HashMap. 
406    pub fn new() -> Self { 
407        Self { data: HashMap::new() } 
408    } 
409
410    /// Inserts a key-value pair into the UrlEncodedForm. 
411    pub fn insert(&mut self, key: String, value: String) { 
412        self.data.insert(key, value); 
413    } 
414
415    /// Gets the value from the UrlEncodedForm. 
416    pub fn get(&self, key: &str) -> Option<&String> { 
417        self.data.get(key) 
418    } 
419
420    pub fn get_or_default(&self, key: &str) -> &String { 
421        if let Some(value) = self.data.get(key) { 
422            return value; 
423        } 
424        static EMPTY: Lazy<String> = Lazy::new(|| "".to_string()); 
425        &EMPTY 
426    } 
427
428    /// Gets all values from the UrlEncodedForm. 
429    pub fn get_all(&self) -> &HashMap<String, String> { 
430        &self.data 
431    } 
432} 
433
434impl From<HashMap<String, String>> for UrlEncodedForm { 
435    fn from(data: HashMap<String, String>) -> Self { 
436        Self { data } 
437    } 
438} 
439
440/// Represents a multipart form data. 
441#[derive(Debug, Clone)] 
442pub struct MultiForm{ 
443    data: HashMap<String, MultiFormField> 
444} 
445
446/// Represents a field in a multipart form.
447#[derive(Debug, Clone)]
448pub enum MultiFormField {
449    Text(String),
450    File(Vec<MultiFormFieldFile>)
451} 
452
453/// Represents a file in a multipart form. 
454#[derive(Debug, Clone)]
455pub struct MultiFormFieldFile {
456    filename: Option<String>,
457    content_type: Option<String>, 
458    data: Vec<u8>,
459} 
460
461impl From<HashMap<String, MultiFormField>> for MultiForm { 
462    fn from(data: HashMap<String, MultiFormField>) -> Self { 
463        Self { data } 
464    } 
465} 
466
467impl MultiForm{ 
468    /// Creates a new MultiForm with an empty HashMap. 
469    pub fn new() -> Self { 
470        Self { data: HashMap::new() } 
471    } 
472
473    /// Inserts a field into the MultiForm. 
474    pub fn insert(&mut self, key: String, value: MultiFormField) { 
475        self.data.insert(key, value); 
476    } 
477
478    /// Gets the field from the MultiForm. 
479    pub fn get(&self, key: &str) -> Option<&MultiFormField> { 
480        self.data.get(key) 
481    } 
482
483    /// Gets all fields from the MultiForm. 
484    pub fn get_all(&self) -> &HashMap<String, MultiFormField> { 
485        &self.data 
486    } 
487
488    /// Gets the files from the MultiForm. 
489    pub fn get_text(&self, key: &str) -> Option<&String> { 
490        if let Some(field) = self.data.get(key) { 
491            if let MultiFormField::Text(value) = field { 
492                return Some(value); 
493            } 
494        } 
495        None 
496    } 
497
498    pub fn get_text_or_default(&self, key: &str) -> String { 
499        if let Some(field) = self.data.get(key) { 
500            if let MultiFormField::Text(value) = field { 
501                return value.clone(); 
502            } 
503        } 
504        "".to_string() 
505    } 
506
507    /// Gets the files from the MultiForm. 
508    pub fn get_files(&self, key: &str) -> Option<&Vec<MultiFormFieldFile>> { 
509        if let Some(field) = self.data.get(key) { 
510            if let MultiFormField::File(files) = field { 
511                return Some(files); 
512            } 
513        } 
514        None 
515    } 
516
517    /// Gets the files from the MultiForm. 
518    /// This function returns an empty vector if the field is not found or if it is not a file. 
519    pub fn get_files_or_default(&self, key: &str) -> &Vec<MultiFormFieldFile> { 
520        if let Some(field) = self.data.get(key) { 
521            if let MultiFormField::File(files) = field { 
522                return files; 
523            } 
524        } 
525        static EMPTY: Lazy<Vec<MultiFormFieldFile>> = Lazy::new(|| Vec::new()); 
526        &EMPTY 
527    } 
528
529    /// Get the first file from the MultiForm. 
530    pub fn get_first_file(&self, key: &str) -> Option<&MultiFormFieldFile> { 
531        if let Some(field) = self.data.get(key) { 
532            if let MultiFormField::File(files) = field { 
533                return files.first(); 
534            } 
535        } 
536        None 
537    } 
538
539    /// Get the first file from the MultiForm. 
540    /// This function returns the first file as a MultiFormFieldFile. 
541    pub fn get_first_file_or_default(&self, key: &str) -> &MultiFormFieldFile { 
542        if let Some(field) = self.get_first_file(key) { 
543            return field; 
544        } 
545        static EMPTY: Lazy<MultiFormFieldFile> = Lazy::new(|| MultiFormFieldFile::default()); 
546        &EMPTY 
547    } 
548
549    /// Get the first file content from the MultiForm. 
550    /// This function returns the first file content as a byte slice. 
551    pub fn get_first_file_content(&self, key: &str) -> Option<&[u8]> { 
552        if let Some(field) = self.data.get(key) { 
553            if let MultiFormField::File(files) = field { 
554                return files.first().map(|file| file.data.as_slice()); 
555            } 
556        } 
557        None 
558    } 
559
560    /// Get the first file content from the MultiForm. 
561    /// This function returns the first file content as a byte vector. 
562    /// This function returns an empty vector if the field is not found or if it is not a file. 
563    pub fn get_first_file_content_or_default(&self, key: &str) -> &[u8] { 
564        if let Some(content) = self.get_first_file_content(key) { 
565            return content; 
566        } 
567        static EMPTY: Lazy<Vec<u8>> = Lazy::new(|| Vec::new()); 
568        &EMPTY 
569    }
570}
571
572impl MultiFormField { 
573    pub fn new_text(value: String) -> Self {
574        Self::Text(value) 
575    } 
576    
577    pub fn new_file(files: MultiFormFieldFile) -> Self {
578        Self::File(vec![files])  
579    } 
580
581    /// Creates a new MultiFormField with a file. 
582    /// This function takes a filename, content type, and data as parameters. 
583    /// It returns a MultiFormField::File variant. 
584    /// When the Field is Text type, it will change it into a File type. 
585    pub fn insert_file(&mut self, file: MultiFormFieldFile) {
586        if let Self::File(files) = self {
587            files.push(file); 
588        } else {
589            *self = Self::File(vec![file]); 
590        }
591    }    
592
593    /// Gets the files value from the MultiFormField. 
594    pub fn get_files(&self) -> Option<&Vec<MultiFormFieldFile>> {
595        if let Self::File(files) = self {
596            Some(files) 
597        } else {
598            None 
599        } 
600    } 
601}
602
603impl Default for MultiFormField { 
604    /// Creates a new MultiFormField with an empty string. 
605    fn default() -> Self { 
606        Self::Text("".to_string()) 
607    } 
608} 
609
610impl MultiFormFieldFile{ 
611    pub fn new(filename: Option<String>, content_type: Option<String>, data: Vec<u8>) -> Self { 
612        Self { filename, content_type, data } 
613    } 
614
615    pub fn filename(&self) -> Option<String> { 
616        self.filename.clone() 
617    } 
618
619    pub fn content_type(&self) -> Option<String> { 
620        self.content_type.clone() 
621    } 
622
623    pub fn data(&self) -> &[u8] { 
624        &self.data 
625    } 
626} 
627
628impl Default for MultiFormFieldFile { 
629    fn default() -> Self { 
630        Self { filename: None, content_type: None, data: Vec::new() } 
631    } 
632} 
633
634pub struct CookieResponse{ 
635    pub name: String, 
636    pub value: String, 
637    pub path: Option<String>, 
638    pub domain: Option<String>, 
639    pub expires: Option<String>, 
640    pub max_age: Option<String>, 
641    pub secure: Option<bool>, 
642    pub http_only: Option<bool>, 
643} 
644
645impl CookieResponse{ 
646    /// Creates a new CookieResponse with the given name and value. 
647    /// This function initializes the cookie with default values for path, domain, expires, max_age, secure, and http_only. 
648    /// It returns a CookieResponse instance. 
649    /// # Examples 
650    /// ```rust 
651    /// use starberry_core::http::http_value::CookieResponse; 
652    /// let cookie = CookieResponse::new("session_id", 123456).domain("example.com".to_string()).path("/".to_string()).expires("Wed, 21 Oct 2025 07:28:00 GMT".to_string()).secure(true).http_only(true); 
653    /// ``` 
654    pub fn new<T: ToString, S: ToString>(name: S, value: T) -> Self { 
655        Self { 
656            name: name.to_string(), 
657            value: value.to_string(), 
658            path: None, 
659            domain: None, 
660            expires: None, 
661            max_age: None, 
662            secure: None, 
663            http_only: None, 
664        } 
665    } 
666
667    pub fn get_name(&self) -> &str { 
668        &self.name 
669    } 
670
671    pub fn set_name<T: ToString>(&mut self, name: T) { 
672        self.name = name.to_string(); 
673    } 
674
675    pub fn get_value(&self) -> &str { 
676        &self.value  
677    } 
678
679    pub fn set_value<T: ToString>(&mut self, value: T) { 
680        self.value = value.to_string(); 
681    } 
682
683    pub fn path<T: ToString>(self, path: T) -> Self { 
684        Self { path: Some(path.to_string()), ..self } 
685    } 
686
687    pub fn get_path(&self) -> Option<String> { 
688        self.path.clone() 
689    } 
690
691    pub fn set_path<T: ToString> (&mut self, path: T) { 
692        self.path = Some(path.to_string()); 
693    } 
694
695    pub fn clear_path(&mut self) { 
696        self.path = None; 
697    } 
698
699    pub fn domain<T: ToString>(self, domain: T) -> Self { 
700        Self { domain: Some(domain.to_string()), ..self } 
701    } 
702
703    pub fn get_domain(&self) -> Option<String> { 
704        self.domain.clone() 
705    } 
706
707    pub fn set_domain<T: ToString> (&mut self, domain: T) { 
708        self.domain = Some(domain.to_string()); 
709    } 
710
711    pub fn clear_domain(&mut self) { 
712        self.domain = None; 
713    } 
714
715    pub fn expires<T: ToString> (self, expires: T) -> Self { 
716        Self { expires: Some(expires.to_string()), ..self } 
717    } 
718
719    pub fn get_expires(&self) -> Option<String> { 
720        self.expires.clone() 
721    } 
722
723    pub fn set_expires<T: ToString> (&mut self, expires: T) { 
724        self.expires = Some(expires.to_string()); 
725    } 
726
727    pub fn clear_expires(&mut self) { 
728        self.expires = None; 
729    } 
730
731    pub fn max_age<T: ToString> (self, max_age: T) -> Self { 
732        Self { max_age: Some(max_age.to_string()), ..self } 
733    } 
734
735    pub fn get_max_age(&self) -> Option<String> { 
736        self.max_age.clone() 
737    } 
738
739    pub fn set_max_age<T: ToString> (&mut self, max_age: T) { 
740        self.max_age = Some(max_age.to_string()); 
741    } 
742
743    pub fn clear_max_age(&mut self) { 
744        self.max_age = None; 
745    } 
746
747    pub fn secure(self, secure: bool) -> Self { 
748        Self { secure: Some(secure), ..self } 
749    } 
750
751    pub fn get_secure(&self) -> Option<bool> { 
752        self.secure.clone() 
753    } 
754
755    pub fn set_secure(&mut self, secure: bool) { 
756        self.secure = Some(secure); 
757    } 
758
759    pub fn clear_secure(&mut self) { 
760        self.secure = None; 
761    } 
762
763    pub fn http_only(self, http_only: bool) -> Self { 
764        Self { http_only: Some(http_only), ..self } 
765    } 
766
767    pub fn get_http_only(&self) -> Option<bool> { 
768        self.http_only.clone() 
769    } 
770
771    pub fn set_http_only(&mut self, http_only: bool) { 
772        self.http_only = Some(http_only); 
773    } 
774
775    pub fn clear_http_only(&mut self) { 
776        self.http_only = None; 
777    } 
778
779    pub fn to_string(&self) -> String { 
780        let mut result = format!("{}={}", self.name, self.value.to_string()); 
781        if let Some(ref path) = self.path { 
782            result.push_str(&format!("; Path={}", path)); 
783        } 
784        if let Some(ref domain) = self.domain { 
785            result.push_str(&format!("; Domain={}", domain)); 
786        } 
787        if let Some(ref expires) = self.expires { 
788            result.push_str(&format!("; Expires={}", expires)); 
789        } 
790        if let Some(ref max_age) = self.max_age { 
791            result.push_str(&format!("; Max-Age={}", max_age)); 
792        } 
793        if let Some(ref secure) = self.secure { 
794            if *secure { 
795                result.push_str("; Secure"); 
796            } 
797        } 
798        if let Some(ref http_only) = self.http_only { 
799            if *http_only { 
800                result.push_str("; HttpOnly"); 
801            } 
802        } 
803        result 
804    } 
805} 
806
807impl std::fmt::Display for CookieResponse { 
808    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 
809        write!(f, "{}", self.to_string()) 
810    } 
811} 
812
813#[derive(Debug, Clone)] 
814pub struct RequestPath{ 
815    path: Vec<String> 
816} 
817
818impl RequestPath{ 
819    pub fn new(path: Vec<String>) -> Self{ 
820        Self { path }  
821    } 
822
823    pub fn to_string(&self) -> String{ 
824        let mut result = String::new(); 
825        for part in &self.path { 
826            result.push('/'); 
827            result.push_str(part); 
828        } 
829        result 
830    } 
831
832    pub fn from_string(url: &str) -> Self{ 
833        let mut path = Vec::new(); 
834        let parts: Vec<&str> = url.split('/').collect(); 
835        for part in parts { 
836            if !part.is_empty() { 
837                path.push(part.to_string()); 
838            } 
839        } 
840        Self { path } 
841    } 
842
843    pub fn url_part(&self, part: usize) -> String{ 
844        if part < 0 { 
845            return self.path[self.path.len() as usize + part as usize].clone(); 
846        } else if part >= self.path.len() { 
847            return "".to_string(); 
848        } 
849        self.path[part].clone()  
850    }
851}