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#[derive(Debug, Clone, PartialEq, Eq)]
232pub enum HttpContentType {
233 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 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(¶meters, "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(¶meters, "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 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 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 pub fn new() -> Self {
407 Self { data: HashMap::new() }
408 }
409
410 pub fn insert(&mut self, key: String, value: String) {
412 self.data.insert(key, value);
413 }
414
415 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 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#[derive(Debug, Clone)]
442pub struct MultiForm{
443 data: HashMap<String, MultiFormField>
444}
445
446#[derive(Debug, Clone)]
448pub enum MultiFormField {
449 Text(String),
450 File(Vec<MultiFormFieldFile>)
451}
452
453#[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 pub fn new() -> Self {
470 Self { data: HashMap::new() }
471 }
472
473 pub fn insert(&mut self, key: String, value: MultiFormField) {
475 self.data.insert(key, value);
476 }
477
478 pub fn get(&self, key: &str) -> Option<&MultiFormField> {
480 self.data.get(key)
481 }
482
483 pub fn get_all(&self) -> &HashMap<String, MultiFormField> {
485 &self.data
486 }
487
488 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 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 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 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 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 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 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 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 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 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 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}