oak_http_server/
structs.rs1use std::{collections::HashMap, fmt};
2
3#[derive(PartialEq, Clone)]
5pub struct Version {
6 pub major: usize,
8 pub minor: usize,
10}
11
12impl Version {
13 pub fn new<S>(version: S) -> Option<Self>
30 where
31 S: Into<String>,
32 {
33 let version = version.into();
34
35 if version.len() >= 5 {
36 if &version[0..4] == "HTTP" && &version[4..5] == "/" {
37 let version_split = &mut version[5..].split(".");
38 if version_split.clone().count() == 2 {
39 let parse_int = |option_input: Option<&str>| -> Option<usize> {
40 let Some(string_num) = option_input else {
41 return None;
42 };
43
44 let Ok(number) = string_num.parse::<usize>() else {
45 return None;
46 };
47
48 Some(number)
49 };
50
51 let Some(major) = parse_int(version_split.next()) else {return None};
52 let Some(minor) = parse_int(version_split.next()) else {return None};
53
54 return Some(Self { major, minor });
55 }
56 }
57 }
58
59 None
60 }
61}
62
63impl fmt::Display for Version {
64 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
65 write!(f, "HTTP/{}.{}", self.major, self.minor)
66 }
67}
68
69#[derive(Clone)]
71pub struct Target {
72 pub target_path: String,
77 pub relative_path: String,
79 pub queries: HashMap<String, String>,
81}
82
83impl Target {
84 pub fn new<S>(target: S) -> Self
86 where
87 S: Into<String>,
88 {
89 let target_string: String = Self::decode_url(target.into());
90
91 let (absolute_path, queries_str) = target_string
92 .split_once('?')
93 .unwrap_or((&target_string, ""));
94
95 let mut queries = HashMap::new();
96
97 if !queries_str.is_empty() {
98 let queries_split = queries_str.split("&");
99
100 for query_str in queries_split {
101 if let Some((name, value)) = query_str.split_once("=") {
102 queries.insert(name.to_string(), value.to_string());
103 }
104 }
105 }
106
107 Self {
108 target_path: String::new(),
109 relative_path: absolute_path.to_string(),
110 queries,
111 }
112 }
113
114 pub fn full_url(&self) -> String {
116 format!("{}{}", &self.target_path, &self.relative_path)
117 }
118
119 fn decode_url(encoded_url: String) -> String {
120 let mut url_iterator = encoded_url.split("%");
121
122 [
123 url_iterator.next().unwrap().to_string(),
124 url_iterator
125 .map(|str_to_decode| {
126 if str_to_decode.len() >= 2 {
127 if str_to_decode[..2]
128 .chars()
129 .all(|char_to_check| char_to_check.is_digit(16))
130 {
131 let mut concatenated_string = String::new();
132 concatenated_string.push(
133 char::from_u32(
134 u32::from_str_radix(&str_to_decode[..2], 16).unwrap(),
135 )
136 .unwrap(),
137 );
138 concatenated_string.push_str(&str_to_decode[2..]);
139 return concatenated_string;
140 }
141 }
142
143 str_to_decode.to_string()
144 })
145 .collect::<Vec<String>>()
146 .join(""),
147 ]
148 .join("")
149 }
150}
151
152impl fmt::Display for Target {
153 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
154 write!(f, "{}{}", self.full_url(), {
155 let mut queries_string = self
156 .queries
157 .iter()
158 .map(|(name, value)| format!("{}: {}&", name, value))
159 .collect::<String>();
160
161 if !queries_string.is_empty() {
162 queries_string.insert_str(0, "?");
163 queries_string.pop();
164 }
165
166 queries_string
167 })
168 }
169}