Initial commit

This commit is contained in:
Nathan Fisher 2024-05-01 16:08:33 -04:00
commit 683aa50d2a
6 changed files with 178 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
target/

7
Cargo.lock generated Normal file
View file

@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "b64"
version = "0.1.0"

8
Cargo.toml Normal file
View file

@ -0,0 +1,8 @@
[package]
name = "b64"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

36
src/decode.rs Normal file
View file

@ -0,0 +1,36 @@
pub use {
super::B64Alphabet,
std::{
io::{self, Read, Write, ErrorKind},
},
};
#[derive(Debug)]
pub enum Error {
Io(io::Error),
IllegalChar(char),
}
impl From<io::Error> for Error {
fn from(value: io::Error) -> Self {
Self::Io(value)
}
}
impl From<char> for Error {
fn from(value: char) -> Self {
Self::IllegalChar(value)
}
}
pub struct Decoder<R: Read, W: Write> {
reader: R,
writer: W,
alphabet: B64Alphabet,
}
#[cfg(test)]
mod tests {
use super::*;
}

81
src/encode.rs Normal file
View file

@ -0,0 +1,81 @@
pub use {
super::B64Alphabet,
std::{
fmt::{self, Write},
io::{self, Read, ErrorKind},
},
};
#[derive(Debug)]
pub enum Error {
Io(io::Error),
Fmt(fmt::Error),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Io(e) => write!(f, "{e}"),
Self::Fmt(e) => write!(f, "{e}"),
}
}
}
impl From<io::Error> for Error {
fn from(value: io::Error) -> Self {
Self::Io(value)
}
}
impl From<fmt::Error> for Error {
fn from(value: fmt::Error) -> Self {
Self::Fmt(value)
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Fmt(e) => Some(e),
Self::Io(e) => Some(e),
}
}
}
pub struct Encoder<R: Read, W: Write> {
reader: R,
writer: W,
alphabet: B64Alphabet,
}
impl<R: Read, W: Write> Encoder<R, W> {
pub fn new(reader: R, writer: W, alphabet: B64Alphabet) -> Self {
Self { reader, writer, alphabet }
}
pub fn encode(&mut self) -> Result<(), Error> {
todo!()
}
}
impl<R: Read> Encoder<R, String> {
pub fn output(self) -> String {
self.writer
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn encode() {
let mut encoder = Encoder {
reader: "Hello, World!".as_bytes(),
writer: String::new(),
alphabet: B64Alphabet::default(),
};
encoder.encode().unwrap();
assert_eq!(encoder.output(), "SGVsbG8sIFdvcmxkIQ==");
}
}

45
src/lib.rs Normal file
View file

@ -0,0 +1,45 @@
mod encode;
mod decode;
pub use {
encode::{Error as B64EncoderError, Encoder as B64Encoder},
decode::{Error as B64DecoderError, Decoder as B64Decoder},
};
#[derive(Clone, Copy)]
pub struct B64Alphabet {
items: [char; 64],
pad: char,
}
pub static B64_RFC4648_ALPHABET: B64Alphabet = B64Alphabet {
items: [
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', '+', '/',
],
pad: '=',
};
impl Default for B64Alphabet {
fn default() -> Self {
B64_RFC4648_ALPHABET
}
}
impl B64Alphabet {
pub fn idx(&self, c: char) -> Option<usize> {
for (idx, x) in self.items.iter().enumerate() {
if *x == c {
return Some(idx)
}
};
None
}
pub fn pad(&self) -> char {
self.pad
}
}