#![feature(no_panic_pow)]
#![deny(
trivial_numeric_casts,
trivial_casts,
missing_debug_implementations,
unsafe_code,
missing_docs
)]
#![allow(clippy::len_without_is_empty)]
extern crate rand;
use rand::prelude::*;
use std::cmp::Ordering;
use std::cmp::PartialEq;
use std::fmt::{Display, Formatter};
pub mod config;
mod decode;
mod encode;
pub mod error;
#[derive(Eq, Debug, Clone)]
pub struct Base64<'a> {
value: Vec<char>,
conf: &'a config::Config<'a>,
}
impl<'a> Base64<'a> {
pub fn default() -> Self {
Base64 {
value: vec!['A'],
conf: config::STANDARD,
}
}
pub fn new_random(len: usize, conf: &'a config::Config<'a>) -> Self {
let mut val: Vec<char> = Vec::new();
for _i in 0..len {
val.push(generate_base64(conf.get_character_set()));
}
let mut b64 = Base64 { value: val, conf };
b64.add_padding(); b64
}
pub fn set_random(&mut self, len: usize) {
let mut val: Vec<char> = Vec::new();
for _i in 0..len {
val.push(generate_base64(self.conf.get_character_set()));
}
self.value = val;
self.add_padding();
}
pub fn len(&self) -> usize {
self.value.len()
}
fn add_padding(&mut self) {
if self.conf.get_padding().is_some() {
while self.len() % 4 != 0 {
self.value.push(self.conf.get_padding().unwrap());
}
}
}
pub fn new_from_string(
new: &str,
conf: &'a config::Config<'a>,
) -> Result<Self, error::Base64Error> {
let mut val: Vec<char> = Vec::new();
for ch in new.chars() {
if !is_valid_base64('\0', conf.get_character_set(), ch)
|| (conf.get_padding().is_some()
&& !is_valid_base64(conf.get_padding().unwrap(), conf.get_character_set(), ch))
{
return Err(error::Base64Error::InvalidBase64CharacterError);
} else {
val.push(ch);
}
}
let mut b64 = Base64 { value: val, conf };
b64.add_padding();
Ok(b64)
}
pub fn set_config(&mut self, conf: &'a config::Config<'a>) {
self.value = self.convert_to_new_config(conf);
self.conf = conf;
self.add_padding();
}
fn convert_to_new_config(&self, conf: &'a config::Config<'a>) -> Vec<char> {
let mut v: Vec<char> = Vec::new();
for i in &self.value {
if self.conf.get_padding().is_some() && *i == self.conf.get_padding().unwrap() {
if conf.get_padding().is_some()
&& conf.get_padding().unwrap() == self.conf.get_padding().unwrap()
{
continue;
} else if conf.get_padding().is_some()
&& conf.get_padding().unwrap() != self.conf.get_padding().unwrap()
{
v.push(conf.get_padding().unwrap());
} else {
continue;
}
} else {
v.push(decimal_to_base64_char(
conf.get_character_set(),
base64_char_to_decimal(self.conf.get_character_set(), *i),
));
}
}
v
}
pub fn set_from_string(&mut self, new: &str) -> bool {
let mut val: Vec<char> = Vec::new();
for ch in new.chars() {
if (self.conf.get_padding().is_none()
&& is_valid_base64('\0', self.conf.get_character_set(), ch))
|| (self.conf.get_padding().is_some()
&& is_valid_base64(
self.conf.get_padding().unwrap(),
self.conf.get_character_set(),
ch,
))
{
val.push(ch);
} else {
return false;
}
}
self.value = val;
self.add_padding();
true
}
pub fn expand_to(&mut self, len: usize) {
while self.value.len() < len {
self.value.push('A');
}
self.value.reverse();
self.add_padding();
}
pub fn truncate_to(&mut self, len: usize) {
if len > 0 && self.value.len() > len {
self.value.reverse(); while self.value.len() > len {
self.value.pop();
}
self.value.reverse(); self.add_padding(); }
}
}
fn generate_base64(a: &[char]) -> char {
decimal_to_base64_char(a, thread_rng().gen_range(0, 64) as u128)
}
fn is_valid_base64(pad: char, a: &[char], val: char) -> bool {
if val == '\n' || val == ' ' || val == pad {
return true;
} else {
for i in a.iter() {
if val == *i {
return true;
}
}
}
false
}
pub(crate) fn decimal_to_base64(conf: &config::Config, mut value: u128) -> Vec<char> {
let mut v: Vec<char> = Vec::new();
while value > 0 {
let base64_val = value % 64;
value /= 64;
v.push(decimal_to_base64_char(conf.get_character_set(), base64_val));
}
v.reverse(); v
}
pub(crate) fn decimal_to_base64_char(a: &[char], value: u128) -> char {
a[value as usize]
}
pub(crate) fn base64_char_to_decimal(a: &[char], c: char) -> u128 {
for (i, val) in a.iter().enumerate() {
if c == *val {
return i as u128;
}
}
0 }
impl<'a> Display for Base64<'a> {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
let mut print: String = String::new();
for i in &self.value {
print.push(*i);
}
write!(f, "{}", print)
}
}
impl<'a> PartialEq for Base64<'a> {
fn eq(&self, other: &Base64) -> bool {
if self.value.len() != other.value.len() {
return false;
} else {
for i in 0..self.value.len() {
if self.value[i] != other.value[i] {
return false;
}
}
}
true
}
}
impl<'a> Ord for Base64<'a> {
fn cmp(&self, other: &Base64<'a>) -> Ordering {
if self.value.len() != other.value.len() {
return self.value.len().cmp(&other.value.len());
} else {
for i in 0..self.value.len() {
if self.value[i] != '\n'
&& other.value[i] != '\n'
&& self.value[i] != other.value[i]
{
return base64_char_to_decimal(self.conf.get_character_set(), self.value[i])
.cmp(&base64_char_to_decimal(
other.conf.get_character_set(),
other.value[i],
));
}
}
}
Ordering::Equal
}
}
impl<'a> PartialOrd for Base64<'a> {
fn partial_cmp(&self, other: &Base64<'a>) -> Option<Ordering> {
Some(self.cmp(other))
}
}