1#[cfg(test)]
20extern crate quickcheck;
21#[cfg(test)]
22#[macro_use(quickcheck)]
23extern crate quickcheck_macros;
24
25use std::fmt;
26use std::fmt::{Debug, Formatter};
27
28use anyhow::Result;
29use base64::{CharacterSet, Config};
30use num_bigint::{BigInt, Sign};
31
32#[derive(Debug, Clone)]
33pub enum Endian {
34 LE,
35 BE,
36}
37
38#[derive(Debug, Clone)]
39pub struct Base64StringFactory {
40 url_safe: bool,
41 padding: bool,
42}
43
44impl Default for Base64StringFactory {
45 fn default() -> Self {
46 Self::new(false, false)
47 }
48}
49
50impl Base64StringFactory {
51 pub fn new(url_safe: bool, padding: bool) -> Self {
52 Self { url_safe, padding }
53 }
54
55 #[inline]
56 fn encode<T: AsRef<[u8]>>(self, input: T) -> Base64String {
57 let cs = if self.url_safe {
58 CharacterSet::UrlSafe
59 } else {
60 CharacterSet::Standard
61 };
62 let config = Config::new(cs, self.padding);
63 let result = base64::encode_config(input, config);
64 Base64String::new(result, self.url_safe, self.padding)
65 }
66
67 pub fn encode_from_string(self, input: &str) -> Base64String {
68 self.encode(input)
69 }
70
71 pub fn encode_from_bytes(self, input: &[u8]) -> Base64String {
72 self.encode(input)
73 }
74
75 pub fn encode_with_endian_from_bigint(self, input: &BigInt, endian: Endian) -> Base64String {
76 let (_, bytes) = match endian {
77 Endian::BE => input.to_bytes_be(),
78 Endian::LE => input.to_bytes_le(),
79 };
80 self.encode_from_bytes(&bytes)
81 }
82}
83
84#[derive(Debug, Clone)]
85pub struct Base64String {
86 value: String,
87 url_safe: bool,
88 padding: bool,
89}
90
91#[derive(Debug, Clone)]
92pub enum Base64DecodeError {
93 DecodeError(String),
94}
95
96impl fmt::Display for Base64String {
97 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
98 write!(
99 f,
100 "Base64String(value = {}, url_safe = {}, padding = {})",
101 self.value, self.url_safe, self.padding
102 )
103 }
104}
105
106impl Base64String {
107 pub fn new(value: String, url_safe: bool, padding: bool) -> Self {
108 Self {
109 value,
110 url_safe,
111 padding,
112 }
113 }
114
115 pub const fn is_url_safe(&self) -> bool {
116 self.url_safe
117 }
118
119 pub const fn is_padding(&self) -> bool {
120 self.padding
121 }
122
123 pub fn len(&self) -> usize {
124 self.value.len()
125 }
126
127 pub fn to_value(&self) -> &str {
128 &self.value
129 }
130
131 pub fn decode_to_bytes(self) -> Result<Vec<u8>> {
132 let cs = if self.url_safe {
133 CharacterSet::UrlSafe
134 } else {
135 CharacterSet::Standard
136 };
137 let config = Config::new(cs, self.padding);
138 match base64::decode_config(self.value, config) {
139 Ok(r) => Ok(r),
140 Err(r) => Err(r)?,
141 }
142 }
143
144 pub fn decode_to_string(self) -> Result<String> {
145 let v = self.decode_to_bytes()?;
146 match String::from_utf8(v) {
147 Ok(r) => Ok(r),
148 Err(r) => Err(r.utf8_error())?,
149 }
150 }
151
152 pub fn decode_with_endian_to_bigint(self, endian: Endian) -> Result<BigInt> {
153 let bytes = self.clone().decode_to_bytes()?;
154 let bigint = match endian {
155 Endian::BE => BigInt::from_bytes_be(Sign::Plus, &bytes),
156 Endian::LE => BigInt::from_bytes_le(Sign::Plus, &bytes),
157 };
158 Ok(bigint)
159 }
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165
166 #[test]
167 fn test() {
168 let str = "0123ABC";
169 let factory = Base64StringFactory::default();
170 let encoded = factory.encode_from_string(str);
171 println!("encoded = {}", encoded);
172 let decoded = encoded.decode_to_string().unwrap();
173 println!("decoded = {}", decoded);
174 assert_eq!(decoded, str);
175 }
176
177 #[quickcheck]
178 fn encode_decode_string(s: String) {
179 let factory = Base64StringFactory::new(false, false);
180 let encoded = factory.encode_from_string(&s);
181 println!("{}", encoded);
182 let decoded = encoded.decode_to_string().ok().unwrap();
183 assert_eq!(decoded, s);
184 }
185
186 #[quickcheck]
187 fn encode_decode_bytes(s: Vec<u8>) {
188 let factory = Base64StringFactory::new(false, false);
189 let encoded = factory.encode_from_bytes(&s);
190 let decoded = encoded.decode_to_bytes().ok().unwrap();
191 assert_eq!(decoded, s);
192 }
193
194 #[quickcheck]
195 fn encode_decode_bigint_with_endian(n: u128) {
196 let s = BigInt::from(n);
197 let factory = Base64StringFactory::new(false, false);
198 let encoded = factory.encode_with_endian_from_bigint(&s, Endian::BE);
199 let decoded = encoded.decode_with_endian_to_bigint(Endian::BE).ok().unwrap();
200 assert_eq!(decoded, s);
201 }
202}