1pub mod path;
9pub mod ppath;
10pub mod url_encoding;
11
12use kstring::KString;
13
14use ppath::PPath;
15use url_encoding::{url_decode, url_encode, UrlDecodingError};
16
17#[derive(Debug)]
23pub struct QueryString(Vec<(KString, KString)>);
24
25impl From<&QueryString> for String {
26 fn from(q: &QueryString) -> Self {
27 let mut s = String::new();
28 let mut is_first = true;
29 for (k, v) in &q.0 {
30 if is_first {
31 is_first = false;
32 } else {
33 s.push('&');
34 }
35 s.push_str(&url_encode(&k));
36 s.push('=');
37 s.push_str(&url_encode(&v));
38 }
39 s
40 }
41}
42
43pub trait ToVecKeyVal {
44 fn to_vec_key_val(self) -> Vec<(KString, KString)>;
45}
46
47impl ToVecKeyVal for Vec<(String, String)> {
48 fn to_vec_key_val(self) -> Vec<(KString, KString)> {
49 self.into_iter()
50 .map(|(k, v)| (KString::from(k), KString::from(v)))
51 .collect()
52 }
53}
54impl ToVecKeyVal for Vec<(&str, &str)> {
55 fn to_vec_key_val(self) -> Vec<(KString, KString)> {
56 self.into_iter()
57 .map(|(k, v)| (KString::from_ref(k), KString::from_ref(v)))
58 .collect()
59 }
60}
61impl<const N: usize> ToVecKeyVal for [(KString, KString); N] {
62 fn to_vec_key_val(self) -> Vec<(KString, KString)> {
63 self.into_iter().collect()
64 }
65}
66impl ToVecKeyVal for &[(KString, KString)] {
67 fn to_vec_key_val(self) -> Vec<(KString, KString)> {
68 self.into_iter().cloned().collect()
69 }
70}
71impl ToVecKeyVal for &[(String, String)] {
72 fn to_vec_key_val(self) -> Vec<(KString, KString)> {
73 self.into_iter()
74 .map(|(k, v)| (KString::from_ref(k), KString::from_ref(v)))
75 .collect()
76 }
77}
78impl ToVecKeyVal for &[(&str, &str)] {
79 fn to_vec_key_val(self) -> Vec<(KString, KString)> {
80 self.into_iter()
81 .map(|(k, v)| (KString::from_ref(k), KString::from_ref(v)))
82 .collect()
83 }
84}
85impl<const N: usize> ToVecKeyVal for [(&str, &str); N] {
86 fn to_vec_key_val(self) -> Vec<(KString, KString)> {
87 self.into_iter()
88 .map(|(k, v)| (KString::from_ref(k), KString::from_ref(v)))
89 .collect()
90 }
91}
92
93impl QueryString {
94 pub fn new(keyvals: impl ToVecKeyVal) -> Self {
95 Self(keyvals.to_vec_key_val())
96 }
97
98 pub fn from_str(s: &str) -> Result<Self, UrlDecodingError> {
99 let mut v = Vec::new();
100 for partraw in s.split('&') {
101 if !partraw.is_empty() {
102 if let Some((key, val)) = partraw.split_once('=') {
103 v.push((url_decode(key)?.into(), url_decode(val)?.into()));
104 } else {
105 v.push((url_decode(partraw)?.into(), "".into()));
107 }
108 }
109 }
110 Ok(QueryString(v))
111 }
112
113 pub fn push(&mut self, keyval: (KString, KString)) {
114 self.0.push(keyval);
115 }
116}
117
118#[cfg(test)]
119mod tests1 {
120 use super::*;
121
122 #[test]
123 fn t_querystring_from_str() {
124 fn t(s: &str, q: &[(&str, &str)]) {
125 let q1 = QueryString::from_str(s).expect("not to fail");
126 let q11: Vec<(&str, &str)> =
127 q1.0.iter().map(|(k, v)| (k.as_str(), v.as_str())).collect();
128 assert_eq!(&q11, q);
129 }
130 t("", &[]);
131 t("foo", &[("foo", "")]); t("foo=1", &[("foo", "1")]);
133 t("=1&&2=", &[("", "1"), ("2", "")]);
134 t("foo=1&bar=2", &[("foo", "1"), ("bar", "2")]);
135 t("foo=1%26&ba%72=%202", &[("foo", "1&"), ("bar", " 2")]);
136 }
139}
140
141#[derive(Debug)]
145pub struct AUriLocal {
146 path: PPath<KString>,
147 query: Option<QueryString>,
148 }
150
151impl AUriLocal {
152 pub fn new(path: PPath<KString>, query: Option<QueryString>) -> Self {
153 Self { path, query }
154 }
155 pub fn from_str(path: &str, query: Option<QueryString>) -> Self {
156 Self {
157 path: PPath::from_str(path),
158 query,
159 }
160 }
161}
162
163impl From<&AUriLocal> for String {
164 fn from(a: &AUriLocal) -> Self {
165 let mut pathstring = a.path.to_string();
166 if let Some(query) = &a.query {
167 pathstring.push('?');
168 pathstring.push_str(&String::from(query));
169 }
170 pathstring
171 }
172}
173
174impl From<AUriLocal> for String {
175 fn from(a: AUriLocal) -> Self {
176 Self::from(&a)
177 }
178}
179
180#[cfg(test)]
181mod tests {
182 use super::*;
183
184 #[test]
185 fn t_1() {
186 let uri = AUriLocal::new(PPath::from_str("/foo/bar"), None);
187 assert_eq!(String::from(&uri).as_str(), "/foo/bar");
188 }
189
190 #[test]
191 fn t_2() {
192 let q = QueryString::new([
193 ("fun", "1"),
194 ("Motörhead", "C'est bien ça & méchanique = plus!"),
195 ]);
196 let uri = AUriLocal::from_str("/foo///bar/", Some(q));
197 assert_eq!(
198 String::from(&uri).as_str(),
199 "/foo/bar/?fun=1&Mot%C3%B6rhead=C%27est%20bien%20%C3%A7a%20%26%20m%C3%A9chanique%20%3D%20plus%21");
200 }
201}
202
203pub enum AUri {
204 Local(AUriLocal),
205 }