dev_bestia_url_utf8/
url_utf8_mod.rs1use core::str::FromStr;
10use percent_encoding::{percent_decode_str, AsciiSet, CONTROLS};
11use std::string::ToString;
12
13use dev_bestia_string_utils::*;
14
15#[macro_export]
21macro_rules! url_u {
22 ($literal:literal) => {
25 UrlUtf8EncodedString::new_0($literal)
27 };
28 ($literal:expr,$part_1:expr) => {
30 UrlUtf8EncodedString::new_1($literal, $part_1)
32 };
33 ($literal:expr,$part_1:expr,$part_2:expr) => {
35 UrlUtf8EncodedString::new_2($literal, $part_1, $part_2)
37 };
38 ($literal:expr,$part_1:expr,$part_2:expr,$part_3:expr) => {
40 UrlUtf8EncodedString::new_3($literal, $part_1, $part_2, $part_3)
42 };
43 ($literal:expr,$part_1:expr,$part_2:expr,$part_3:expr,$part_4:expr) => {
45 UrlUtf8EncodedString::new_4($literal, $part_1, $part_2, $part_3, $part_4)
47 };
48}
49
50#[derive(Clone, Debug)]
56pub struct UrlUtf8EncodedString {
57 s: String,
59}
60
61impl UrlUtf8EncodedString {
62 pub fn new_0(literal: &str) -> UrlUtf8EncodedString {
64 UrlUtf8EncodedString { s: s!(literal) }
65 }
66 pub fn new_1(literal: &str, part_1: &str) -> UrlUtf8EncodedString {
68 UrlUtf8EncodedString {
69 s: literal.replacen("{}", &Self::encode_fragment(part_1), 1),
70 }
71 }
72 pub fn new_2(literal: &str, part_1: &str, part_2: &str) -> UrlUtf8EncodedString {
74 UrlUtf8EncodedString {
75 s: literal.replacen("{}", &Self::encode_fragment(part_1), 1).replacen("{}", &Self::encode_fragment(part_2), 1),
76 }
77 }
78 pub fn new_3(literal: &str, part_1: &str, part_2: &str, part_3: &str) -> UrlUtf8EncodedString {
80 UrlUtf8EncodedString {
81 s: literal
82 .replacen("{}", &Self::encode_fragment(part_1), 1)
83 .replacen("{}", &Self::encode_fragment(part_2), 1)
84 .replacen("{}", &Self::encode_fragment(part_3), 1),
85 }
86 }
87 pub fn new_4(literal: &str, part_1: &str, part_2: &str, part_3: &str, part_4: &str) -> UrlUtf8EncodedString {
89 UrlUtf8EncodedString {
90 s: literal
91 .replacen("{}", &Self::encode_fragment(part_1), 1)
92 .replacen("{}", &Self::encode_fragment(part_2), 1)
93 .replacen("{}", &Self::encode_fragment(part_3), 1)
94 .replacen("{}", &Self::encode_fragment(part_4), 1),
95 }
96 }
97 pub fn encode_fragment(s: &str) -> String {
99 s!(percent_encoding::utf8_percent_encode(s, FRAGMENT))
101 }
102}
103impl ToString for UrlUtf8EncodedString {
104 #[inline]
105 fn to_string(&self) -> String {
107 self.s.clone()
109 }
110}
111
112const FRAGMENT: &AsciiSet = &CONTROLS.add(b' ').add(b'"').add(b'<').add(b'>').add(b'`');
117
118#[derive(Clone, Debug)]
123pub struct UrlPartUtf8Decoded {
124 s: String,
126}
127
128impl UrlPartUtf8Decoded {
129 fn new(s: &str) -> Result<Self, crate::UrlUtf8Error> {
132 let s = s!(percent_decode_str(s).decode_utf8()?);
133 Ok(UrlPartUtf8Decoded { s })
134 }
135 #[allow(unused)]
136 pub fn new_from_decoded_string(s: &str) -> Self {
138 UrlPartUtf8Decoded { s: s!(s) }
139 }
140 #[allow(unused)]
141 pub fn get_encoded_string(&self) -> String {
143 UrlUtf8EncodedString::encode_fragment(&self.s)
144 }
145}
146impl FromStr for UrlPartUtf8Decoded {
149 type Err = crate::UrlUtf8Error;
150 #[inline]
151 fn from_str(s: &str) -> Result<Self, crate::UrlUtf8Error> {
155 UrlPartUtf8Decoded::new(s)
156 }
157}
158impl ToString for UrlPartUtf8Decoded {
159 #[inline]
160 fn to_string(&self) -> String {
162 self.s.clone()
164 }
165}
166
167#[cfg(test)]
170mod tests {
171 use super::*;
172 use unwrap::unwrap;
173
174 #[test]
175 fn test_decode_01() {
176 let s = s!(unwrap!(UrlPartUtf8Decoded::new("a%20b%3Cc")));
177 assert_eq!(&s, "a b<c");
178 }
179
180 #[test]
181 fn test_encode_02() {
182 let s = url_u!("/one/two/{}/", "a b<c>d'e\"f");
183 let norm_str = s!(s);
184 assert_eq!(&norm_str, "/one/two/a%20b%3Cc%3Ed\'e%22f/");
185 }
186
187 #[test]
188 fn test_03() {
189 let s = url_u!("/one/two/{}/{}/", "a b<ccc", ">ddd'e\"f");
190 let norm_str = s!(s);
191 assert_eq!(norm_str, "/one/two/a%20b%3Cccc/%3Eddd\'e%22f/");
192 }
193
194 #[test]
195 fn test_04() {
196 let s = url_u!("/one{}one/two/{}/{}/", "1 1 ", "a b<ccc", ">ddd'e\"f");
197 let norm_str = s!(s);
198
199 assert_eq!(norm_str, "/one1%201%20one/two/a%20b%3Cccc/%3Eddd\'e%22f/");
200 }
201 #[test]
202 fn test_05() {
203 let s = url_u!("/one{}one/two{}two/{}/{}/", "1 1 ", " 2 2", "a b<ccc", ">ddd'e\"f");
204 let norm_str = s!(s);
205 assert_eq!(norm_str, "/one1%201%20one/two%202%202two/a%20b%3Cccc/%3Eddd\'e%22f/");
206 }
207}