lb64/lib.rs
1//! Focuses on making a Base64 number as a type, with extensive documentation, and never panicing
2//! code
3//!
4//! Base64 Library that has encoding and decoding of unsigned decimal values and bytes
5//! Along with a full fledged Base64 type in order to store the input of encoding an unsigned
6//! decimal value or bytes. Base64 type implements Clone, Eqs, Debug, along with multiple
7//! constructors for creation, all start with 'new', for example, from_string is from a Base64
8//! compliant &str. Has default Configurations for Base64 numbers see more
9//! [here](https://en.wikipedia.org/wiki/Base64) for `Standard`, `URL_SAFE` with and without padding,
10//! `IMAP` and `MIME` compliant configs along with full fledged support of creating your own Base64 type.
11//! Furthermore, supports random generation of a n long Base64 number.
12//! ```
13//! extern crate lb64;
14//!
15//! use lb64::Base64;
16//! use lb64::config::{Config, MIME};
17//!
18//! fn main() {
19//! let s: &str = "Hello!";
20//! let b64 = Base64::new_encode_bytes(s.as_bytes(), MIME);
21//! println!("{}", b64);
22//! let mut v: u128 = 0;
23//! match b64.decode_to_unsigned() {
24//! Ok(value) => v = value,
25//! Err(e) => println!("{}", e),
26//! }
27//! let b64_other = Base64::new_encode_unsigned(&v, MIME);
28//! if b64_other == b64 {
29//! println!("They're equal!");
30//! }
31//! match String::from_utf8(b64.decode_to_bytes()) {
32//! Ok(value) => println!("{}", value), // prints Hello
33//! Err(e) => println!("{}", e),
34//! }
35//! }
36//! ```
37//! See Examples for more.
38
39// Allow use of pow to prevent panics from overflowing
40#![feature(no_panic_pow)]
41// Prevent trivial casts, require implement debug, completely safe code, and require documentation
42#![deny(
43 trivial_numeric_casts,
44 trivial_casts,
45 missing_debug_implementations,
46 unsafe_code,
47 missing_docs
48)]
49// Requiring a is_empty function doesn't make sense in this context
50#![allow(clippy::len_without_is_empty)]
51extern crate rand;
52
53use rand::prelude::*;
54
55use std::cmp::Ordering;
56use std::cmp::PartialEq;
57use std::fmt::{Display, Formatter};
58
59/// Creation of custom configs for Base64 numbers containing different characters, with or without
60/// padding, with or without a maximum line length. In addition, 5 configs are already defined
61/// because of their popularity (`STANDARD`, `MIME`, `IMAP`, `URLSAFE` with and without padding).
62pub mod config;
63/// Decoding functions for Base64
64mod decode;
65/// Enconding functions for Base64
66mod encode;
67/// Enums for Errors that can occur when making a Config or when decoding
68pub mod error;
69
70/// Base64 number
71///
72/// value: a Vec<char> representing the value of the Base64 number
73///
74/// conf: the config specific for this Base64 number
75///
76/// Implements Clone, Debug, Eqs, and Compare
77#[derive(Eq, Debug, Clone)]
78pub struct Base64<'a> {
79 value: Vec<char>,
80 conf: &'a config::Config<'a>,
81}
82
83impl<'a> Base64<'a> {
84 /// Creates a default Base64 number equivalent to 0 ("A") with
85 /// [STANDARD](../base64/config/constant.STANDARD.html)
86 ///
87 /// # Returns:
88 /// The new Base64 number with the Standard configuration and value of "A"
89 ///
90 /// # Example:
91 /// ```
92 /// extern crate lb64; // Import/Include crate
93 /// use lb64::{Base64}; // Base64
94 ///
95 /// fn main() {
96 /// let b64 = Base64::default(); // Creates new Base64 of value "A"
97 /// println!("{}", b64);
98 /// }
99 /// ```
100 pub fn default() -> Self {
101 Base64 {
102 value: vec!['A'],
103 conf: config::STANDARD,
104 }
105 }
106
107 /// Creates a random base64 number of at least the provided length.
108 ///
109 /// # Parameters:
110 /// new length of base64 number and the configuration struct, Note if the configuration
111 /// specifies padding then the length may be higher if the length specififed isn't divisible by
112 /// 4
113 ///
114 /// #Returns:
115 /// the new random base64 number
116 ///
117 /// # Example:
118 /// ```
119 /// extern crate lb64; // Import/Include crate
120 /// use lb64::{Base64}; // Base64
121 /// use lb64::config::{URL_SAFE_NO_PADDING, URL_SAFE_PADDING}; // Constant configs
122 ///
123 /// fn main() {
124 /// let b64 = Base64::new_random(5, URL_SAFE_NO_PADDING); // Sets the length to 5 and randomly generates the values
125 /// println!("{}", b64); // Since there's no padding then the length will be 5
126 /// let b64 = Base64::new_random(5, URL_SAFE_PADDING); // Generates a random value of length 5, but with padding
127 /// println!("{}", b64); // Since there's padding then the length will be divisible by 4 therefore length 8
128 /// }
129 /// ```
130 pub fn new_random(len: usize, conf: &'a config::Config<'a>) -> Self {
131 let mut val: Vec<char> = Vec::new();
132 for _i in 0..len {
133 val.push(generate_base64(conf.get_character_set()));
134 }
135 let mut b64 = Base64 { value: val, conf };
136 b64.add_padding(); // Add padding if necessary
137 b64
138 }
139
140 /// Sets the value of the Base64 number to a random value. Param: len, length for base64 number
141 ///
142 /// # Parameters:
143 /// The minimum length for the base64 number
144 ///
145 /// # Example:
146 /// ```
147 /// extern crate lb64; // Import/Include crate
148 /// use lb64::{Base64}; // Base64
149 /// use lb64::config::{URL_SAFE_NO_PADDING, URL_SAFE_PADDING}; // Constant configs
150 ///
151 /// fn main() {
152 /// let mut b64 = Base64::new_random(5, URL_SAFE_NO_PADDING); // Sets the length to 5 and randomly generates the values
153 /// println!("{}", b64); // Since there's no padding then the length will be 5
154 /// b64.set_random(8);
155 /// println!("{}", b64); // No padding length will now be 8
156 /// }
157 /// ```
158 pub fn set_random(&mut self, len: usize) {
159 let mut val: Vec<char> = Vec::new();
160 for _i in 0..len {
161 val.push(generate_base64(self.conf.get_character_set()));
162 }
163 self.value = val;
164 self.add_padding();
165 }
166
167 /// Get length of Base64 number
168 ///
169 /// # Return:
170 /// Return usize of Base64 number
171 ///
172 /// # Example:
173 /// ```
174 /// extern crate lb64; // Import/Include crate
175 /// use lb64::{Base64}; // Base64
176 /// use lb64::config::{URL_SAFE_NO_PADDING}; // Constant config
177 ///
178 /// fn main() {
179 /// let mut b64 = Base64::new_random(5, URL_SAFE_NO_PADDING); // Sets the length to 5 and randomly generates the values
180 /// println!("{}", b64); // Since there's no padding then the length will be 5
181 /// println!("{}", b64.len()); // Length of 5
182 /// }
183 /// ```
184 pub fn len(&self) -> usize {
185 self.value.len()
186 }
187
188 /// Adds the padding character if the Base64 config has padding turned on until the number is
189 /// divisible by 4
190 fn add_padding(&mut self) {
191 if self.conf.get_padding().is_some() {
192 while self.len() % 4 != 0 {
193 self.value.push(self.conf.get_padding().unwrap());
194 }
195 }
196 }
197
198 /// Sets Base64 to that String if it's valid
199 ///
200 /// # Return:
201 /// If all characters are valid Base64 return Self otherwise a
202 /// [Base64Error::InvalidBase64CharacterError](error/enum.Base64Error.html#variant.InvalidBase64CharacterError)
203 ///
204 /// # Example:
205 /// ```
206 /// extern crate lb64; // Import/Include crate
207 /// use lb64::{Base64}; // Base64
208 /// use lb64::config::{URL_SAFE_NO_PADDING, URL_SAFE_PADDING}; // Constant configs
209 ///
210 /// fn main() {
211 /// let b64 = Base64::new_from_string("Hello", URL_SAFE_PADDING); // Sets b64 to the string if the string is valid Base64
212 /// // It returns a Result<Base64, String>
213 /// match b64 {
214 /// Ok(value) => println!("{}", 64), // prints "Hello===" adds padding so it's divisible by 4
215 /// Err(e) => println!("{}", e), // The error message stating the first incorrect character
216 /// }
217 /// }
218 /// ```
219 pub fn new_from_string(
220 new: &str,
221 conf: &'a config::Config<'a>,
222 ) -> Result<Self, error::Base64Error> {
223 let mut val: Vec<char> = Vec::new();
224 for ch in new.chars() {
225 if !is_valid_base64('\0', conf.get_character_set(), ch)
226 || (conf.get_padding().is_some()
227 && !is_valid_base64(conf.get_padding().unwrap(), conf.get_character_set(), ch))
228 {
229 return Err(error::Base64Error::InvalidBase64CharacterError);
230 } else {
231 val.push(ch);
232 }
233 }
234 let mut b64 = Base64 { value: val, conf };
235 b64.add_padding();
236 Ok(b64)
237 }
238
239 /// Takes a new configuration and converts the Base64 number to that representation
240 ///
241 /// # Example
242 /// ```
243 /// extern crate lb64; // Import/Include crate
244 /// use lb64::{Base64}; // Base64
245 /// use lb64::config::{URL_SAFE_NO_PADDING, URL_SAFE_PADDING}; // Constant configs
246 ///
247 /// fn main() {
248 /// let mut b64 = Base64::new_encode_unsigned(&63, URL_SAFE_NO_PADDING); // Sets b64 to _
249 /// b64.set_config(URL_SAFE_PADDING); // Changes configs and adds padding to _
250 /// println!("{}", b64); // Prints _===
251 /// }
252 /// ```
253 pub fn set_config(&mut self, conf: &'a config::Config<'a>) {
254 // If they aren't the same actually convert
255 self.value = self.convert_to_new_config(conf);
256 self.conf = conf;
257 self.add_padding();
258 }
259
260 fn convert_to_new_config(&self, conf: &'a config::Config<'a>) -> Vec<char> {
261 let mut v: Vec<char> = Vec::new();
262 for i in &self.value {
263 if self.conf.get_padding().is_some() && *i == self.conf.get_padding().unwrap() {
264 if conf.get_padding().is_some()
265 && conf.get_padding().unwrap() == self.conf.get_padding().unwrap()
266 {
267 // Both have padding and their padding is the same
268 continue;
269 } else if conf.get_padding().is_some()
270 && conf.get_padding().unwrap() != self.conf.get_padding().unwrap()
271 {
272 // They both have padding and they're different
273 v.push(conf.get_padding().unwrap());
274 } else {
275 // The new configuration doesn't have padding and therefore skip it
276 continue;
277 }
278 } else {
279 // Convert the current configuration value to it's new equivalent
280 v.push(decimal_to_base64_char(
281 conf.get_character_set(),
282 base64_char_to_decimal(self.conf.get_character_set(), *i),
283 ));
284 }
285 }
286 v
287 }
288
289 /// Sets the Base64 value to a given String
290 ///
291 /// # Return:
292 /// false if any value is invalid
293 ///
294 /// # Example:
295 /// ```
296 /// extern crate lb64; // Import/Include crate
297 /// use lb64::{Base64}; // Base64
298 /// use lb64::config::{URL_SAFE_NO_PADDING, URL_SAFE_PADDING}; // Constant configs
299 ///
300 /// fn main() {
301 /// let mut b64 = Base64::new_encode_unsigned(&63, URL_SAFE_NO_PADDING); // Sets b64 to _
302 /// b64.set_config(URL_SAFE_PADDING); // Changes configs and adds padding to _
303 /// println!("{}", b64); // Prints _===
304 /// }
305 /// ```
306 pub fn set_from_string(&mut self, new: &str) -> bool {
307 let mut val: Vec<char> = Vec::new();
308 for ch in new.chars() {
309 if (self.conf.get_padding().is_none()
310 && is_valid_base64('\0', self.conf.get_character_set(), ch))
311 || (self.conf.get_padding().is_some()
312 && is_valid_base64(
313 self.conf.get_padding().unwrap(),
314 self.conf.get_character_set(),
315 ch,
316 ))
317 {
318 val.push(ch);
319 } else {
320 return false;
321 }
322 }
323 self.value = val;
324 self.add_padding();
325 true
326 }
327
328 /// Extends base 64 number by prepending As to it to fit a new size
329 ///
330 /// # Parameters:
331 /// len, the new size of the base64 value
332 ///
333 /// # Example:
334 /// ```
335 /// extern crate lb64; // Import/Include crate
336 /// use lb64::{Base64}; // Base64
337 /// use lb64::config::{URL_SAFE_NO_PADDING, URL_SAFE_PADDING}; // Constant configs
338 ///
339 /// fn main() {
340 /// let mut b64 = Base64::new_encode_unsigned(&63, URL_SAFE_NO_PADDING); // Sets b64 to _
341 /// b64.set_config(URL_SAFE_PADDING); // Changes configs and adds padding to _
342 /// println!("{}", b64); // Prints _===
343 /// }
344 /// ```
345 pub fn expand_to(&mut self, len: usize) {
346 while self.value.len() < len {
347 self.value.push('A');
348 }
349 self.value.reverse();
350 self.add_padding();
351 }
352
353 /// Truncates base64 number be removing the most significant values until it fits the new size
354 ///
355 /// # Parameters:
356 /// len, the new size of the base64 value. Must be greater than 0
357 ///
358 /// # Example:
359 /// ```
360 /// extern crate lb64; // Import/Include crate
361 /// use lb64::{Base64}; // Base64
362 /// use lb64::config::{URL_SAFE_PADDING, URL_SAFE_NO_PADDING}; // Constant configs
363 ///
364 /// fn main() {
365 /// let mut b64 = Base64::new_encode_unsigned(&63, URL_SAFE_PADDING); // Sets b64 to _===
366 /// println!("{}", b64); // Prints _===
367 /// b64.truncate_to(2); // This does essentially nothing because padding is required and therefore it must be divisible by 4
368 /// println!("{}", b64); // Prints _=== stil
369 /// let mut b64 = Base64::new_encode_unsigned(&63, URL_SAFE_NO_PADDING); // Sets b64 to _
370 /// b64.truncate_to(1); // Length is already 1 so it remains _
371 /// }
372 /// ```
373 pub fn truncate_to(&mut self, len: usize) {
374 if len > 0 && self.value.len() > len {
375 self.value.reverse(); // flip to remove most significant values
376 while self.value.len() > len {
377 self.value.pop();
378 }
379 self.value.reverse(); // flip back
380 self.add_padding(); // Add padding
381 }
382 }
383}
384
385/// Generates values from 0 to 63 and returns the character corresponding to it
386fn generate_base64(a: &[char]) -> char {
387 decimal_to_base64_char(a, thread_rng().gen_range(0, 64) as u128)
388}
389
390/// Checks if a character is a valid value in Base64
391/// Param: val, the character to check as a u8
392/// Return: true if it's value false otherwise
393fn is_valid_base64(pad: char, a: &[char], val: char) -> bool {
394 if val == '\n' || val == ' ' || val == pad {
395 return true;
396 } else {
397 for i in a.iter() {
398 if val == *i {
399 return true;
400 }
401 }
402 }
403 false
404}
405
406/// Convert decimal value to base64 by mod 64 to get the base64 place and then dividing
407/// by 64 to get the value
408/// Param: value, the value to convert
409/// Return Vector of chars that is the Base64 value
410pub(crate) fn decimal_to_base64(conf: &config::Config, mut value: u128) -> Vec<char> {
411 let mut v: Vec<char> = Vec::new();
412 while value > 0 {
413 let base64_val = value % 64;
414 value /= 64;
415 v.push(decimal_to_base64_char(conf.get_character_set(), base64_val));
416 }
417 v.reverse(); // Reverse to get into proper order
418 v
419}
420
421/// Converts a decimal value to it's base 64 value
422/// Param: value, the value to convert
423/// Return: the character corresponding to the decimal in Base64
424pub(crate) fn decimal_to_base64_char(a: &[char], value: u128) -> char {
425 a[value as usize]
426}
427
428/// Converts a char to it's corresponding u128 value in base64
429/// Param: value, char to convert
430/// Return: u128, the value of the char in base64
431pub(crate) fn base64_char_to_decimal(a: &[char], c: char) -> u128 {
432 for (i, val) in a.iter().enumerate() {
433 if c == *val {
434 return i as u128;
435 }
436 }
437 0 // Not Possible
438}
439
440impl<'a> Display for Base64<'a> {
441 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
442 let mut print: String = String::new();
443 for i in &self.value {
444 print.push(*i);
445 }
446 write!(f, "{}", print)
447 }
448}
449
450impl<'a> PartialEq for Base64<'a> {
451 fn eq(&self, other: &Base64) -> bool {
452 if self.value.len() != other.value.len() {
453 return false;
454 } else {
455 for i in 0..self.value.len() {
456 if self.value[i] != other.value[i] {
457 return false;
458 }
459 }
460 }
461 true
462 }
463}
464
465impl<'a> Ord for Base64<'a> {
466 fn cmp(&self, other: &Base64<'a>) -> Ordering {
467 if self.value.len() != other.value.len() {
468 // Different lengths
469 return self.value.len().cmp(&other.value.len());
470 } else {
471 for i in 0..self.value.len() {
472 if self.value[i] != '\n'
473 && other.value[i] != '\n'
474 && self.value[i] != other.value[i]
475 {
476 // Convert each to their decimal value then cmp
477 return base64_char_to_decimal(self.conf.get_character_set(), self.value[i])
478 .cmp(&base64_char_to_decimal(
479 other.conf.get_character_set(),
480 other.value[i],
481 ));
482 }
483 }
484 }
485 Ordering::Equal
486 }
487}
488
489impl<'a> PartialOrd for Base64<'a> {
490 fn partial_cmp(&self, other: &Base64<'a>) -> Option<Ordering> {
491 Some(self.cmp(other))
492 }
493}