actix_cloud_extra/
utils.rs1use base64::{Engine as _, prelude::BASE64_STANDARD};
2use infer::Type;
3
4pub trait StringUtil {
5 fn is_hex(&self) -> bool;
7
8 fn like_escape(&self) -> String;
11}
12
13impl<S> StringUtil for S
14where
15 S: AsRef<str>,
16{
17 fn is_hex(&self) -> bool {
19 self.as_ref().chars().all(|c| c.is_ascii_hexdigit())
20 }
21
22 fn like_escape(&self) -> String {
24 let mut s = self.as_ref().replace('\\', "\\\\"); s = s.replace('%', "\\%");
26 s.replace('_', "\\_")
27 }
28}
29
30pub struct DataUrl {
31 pub data: Vec<u8>,
32 pub mime: Type,
33}
34
35impl DataUrl {
36 pub fn encode(&self) -> String {
37 let data = BASE64_STANDARD.encode(&self.data);
38 format!("data:{};base64,{data}", self.mime)
39 }
40
41 pub fn decode<S: AsRef<str>>(data: S) -> Option<Self> {
42 let data: Vec<_> = data.as_ref().split(',').collect();
43 if data.len() == 2 {
44 BASE64_STANDARD.decode(data[1]).map_or(None, Self::new)
45 } else {
46 None
47 }
48 }
49
50 pub fn new(data: Vec<u8>) -> Option<Self> {
51 let mime = infer::get(&data);
52 mime.map(|mime| Self { data, mime })
53 }
54}
55
56#[cfg(test)]
57mod tests {
58 use super::*;
59
60 #[test]
61 fn is_hex_valid() {
62 assert!("0123456789abcdefABCDEF".is_hex());
63 assert!("".is_hex());
64 }
65
66 #[test]
67 fn is_hex_invalid() {
68 assert!(!"0x123".is_hex());
69 assert!(!"hello".is_hex());
70 assert!(!"12g4".is_hex());
71 }
72
73 #[test]
74 fn like_escape_basic() {
75 assert_eq!("plain".like_escape(), "plain");
76 }
77
78 #[test]
79 fn like_escape_special() {
80 assert_eq!("a%b_c\\d".like_escape(), "a\\%b\\_c\\\\d");
81 }
82
83 #[test]
84 fn like_escape_backslash_first() {
85 assert_eq!("%\\".like_escape(), "\\%\\\\");
87 }
88
89 #[test]
90 fn dataurl_roundtrip() {
91 let png = vec![0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A];
93 let url = DataUrl::new(png.clone()).unwrap();
94 let encoded = url.encode();
95 let decoded = DataUrl::decode(&encoded).unwrap();
96 assert_eq!(decoded.data, png);
97 assert_eq!(decoded.mime.mime_type(), "image/png");
98 }
99
100 #[test]
101 fn dataurl_decode_bad() {
102 assert!(DataUrl::decode("no-comma-here").is_none());
103 }
104
105 #[test]
106 fn dataurl_new_unknown() {
107 assert!(DataUrl::new(Vec::new()).is_none());
109 assert!(DataUrl::new(vec![0x01, 0x02, 0x03]).is_none());
110 }
111}