use core::str::FromStr;
use percent_encoding::{percent_decode_str, AsciiSet, CONTROLS};
use std::string::ToString;
use dev_bestia_string_utils::*;
#[macro_export]
macro_rules! url_u {
($literal:literal) => {
UrlUtf8EncodedString::new_0($literal)
};
($literal:expr,$part_1:expr) => {
UrlUtf8EncodedString::new_1($literal, $part_1)
};
($literal:expr,$part_1:expr,$part_2:expr) => {
UrlUtf8EncodedString::new_2($literal, $part_1, $part_2)
};
($literal:expr,$part_1:expr,$part_2:expr,$part_3:expr) => {
UrlUtf8EncodedString::new_3($literal, $part_1, $part_2, $part_3)
};
($literal:expr,$part_1:expr,$part_2:expr,$part_3:expr,$part_4:expr) => {
UrlUtf8EncodedString::new_4($literal, $part_1, $part_2, $part_3, $part_4)
};
}
#[derive(Clone, Debug)]
pub struct UrlUtf8EncodedString {
s: String,
}
impl UrlUtf8EncodedString {
pub fn new_0(literal: &str) -> UrlUtf8EncodedString {
UrlUtf8EncodedString { s: s!(literal) }
}
pub fn new_1(literal: &str, part_1: &str) -> UrlUtf8EncodedString {
UrlUtf8EncodedString {
s: literal.replacen("{}", &Self::encode_fragment(part_1), 1),
}
}
pub fn new_2(literal: &str, part_1: &str, part_2: &str) -> UrlUtf8EncodedString {
UrlUtf8EncodedString {
s: literal.replacen("{}", &Self::encode_fragment(part_1), 1).replacen("{}", &Self::encode_fragment(part_2), 1),
}
}
pub fn new_3(literal: &str, part_1: &str, part_2: &str, part_3: &str) -> UrlUtf8EncodedString {
UrlUtf8EncodedString {
s: literal
.replacen("{}", &Self::encode_fragment(part_1), 1)
.replacen("{}", &Self::encode_fragment(part_2), 1)
.replacen("{}", &Self::encode_fragment(part_3), 1),
}
}
pub fn new_4(literal: &str, part_1: &str, part_2: &str, part_3: &str, part_4: &str) -> UrlUtf8EncodedString {
UrlUtf8EncodedString {
s: literal
.replacen("{}", &Self::encode_fragment(part_1), 1)
.replacen("{}", &Self::encode_fragment(part_2), 1)
.replacen("{}", &Self::encode_fragment(part_3), 1)
.replacen("{}", &Self::encode_fragment(part_4), 1),
}
}
pub fn encode_fragment(s: &str) -> String {
s!(percent_encoding::utf8_percent_encode(s, FRAGMENT))
}
}
impl ToString for UrlUtf8EncodedString {
#[inline]
fn to_string(&self) -> String {
self.s.clone()
}
}
const FRAGMENT: &AsciiSet = &CONTROLS.add(b' ').add(b'"').add(b'<').add(b'>').add(b'`');
#[derive(Clone, Debug)]
pub struct UrlPartUtf8Decoded {
s: String,
}
impl UrlPartUtf8Decoded {
fn new(s: &str) -> Result<Self, crate::UrlUtf8Error> {
let s = s!(percent_decode_str(s).decode_utf8()?);
Ok(UrlPartUtf8Decoded { s })
}
#[allow(unused)]
pub fn new_from_decoded_string(s: &str) -> Self {
UrlPartUtf8Decoded { s: s!(s) }
}
#[allow(unused)]
pub fn get_encoded_string(&self) -> String {
UrlUtf8EncodedString::encode_fragment(&self.s)
}
}
impl FromStr for UrlPartUtf8Decoded {
type Err = crate::UrlUtf8Error;
#[inline]
fn from_str(s: &str) -> Result<Self, crate::UrlUtf8Error> {
UrlPartUtf8Decoded::new(s)
}
}
impl ToString for UrlPartUtf8Decoded {
#[inline]
fn to_string(&self) -> String {
self.s.clone()
}
}
#[cfg(test)]
mod tests {
use super::*;
use unwrap::unwrap;
#[test]
fn test_decode_01() {
let s = s!(unwrap!(UrlPartUtf8Decoded::new("a%20b%3Cc")));
assert_eq!(&s, "a b<c");
}
#[test]
fn test_encode_02() {
let s = url_u!("/one/two/{}/", "a b<c>d'e\"f");
let norm_str = s!(s);
assert_eq!(&norm_str, "/one/two/a%20b%3Cc%3Ed\'e%22f/");
}
#[test]
fn test_03() {
let s = url_u!("/one/two/{}/{}/", "a b<ccc", ">ddd'e\"f");
let norm_str = s!(s);
assert_eq!(norm_str, "/one/two/a%20b%3Cccc/%3Eddd\'e%22f/");
}
#[test]
fn test_04() {
let s = url_u!("/one{}one/two/{}/{}/", "1 1 ", "a b<ccc", ">ddd'e\"f");
let norm_str = s!(s);
assert_eq!(norm_str, "/one1%201%20one/two/a%20b%3Cccc/%3Eddd\'e%22f/");
}
#[test]
fn test_05() {
let s = url_u!("/one{}one/two{}two/{}/{}/", "1 1 ", " 2 2", "a b<ccc", ">ddd'e\"f");
let norm_str = s!(s);
assert_eq!(norm_str, "/one1%201%20one/two%202%202two/a%20b%3Cccc/%3Eddd\'e%22f/");
}
}