base64_string_rs/
lib.rs

1//! # About Base64String
2//!
3//! Base64String is a string type in Base64 format that contains meta-information about the encoding.
4//!
5//! # Usage
6//!
7//! ```rust
8//! use base64_string_rs::Base64StringFactory;
9//!
10//! let str = "0123ABC";
11//! let factory = Base64StringFactory::default();
12//! let encoded = factory.encode_from_string(str);
13//! println!("encoded = {}", encoded);
14//! // encoded = Base64String(value = MDEyM0FCQw, url_safe = false, padding = false)
15//! let decoded = encoded.decode_to_string().unwrap();
16//! println!("decoded = {}", decoded); // 0123ABC
17//! # assert_eq!(decoded, str);
18//! ```
19#[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}