commit 683aa50d2a485977714e155a40a6ed34a0bf7594 Author: Nathan Fisher Date: Wed May 1 16:08:33 2024 -0400 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2f7896d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..f638ccd --- /dev/null +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..7eeb5fa --- /dev/null +++ b/Cargo.toml @@ -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] diff --git a/src/decode.rs b/src/decode.rs new file mode 100644 index 0000000..0988833 --- /dev/null +++ b/src/decode.rs @@ -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 for Error { + fn from(value: io::Error) -> Self { + Self::Io(value) + } +} + +impl From for Error { + fn from(value: char) -> Self { + Self::IllegalChar(value) + } +} + +pub struct Decoder { + reader: R, + writer: W, + alphabet: B64Alphabet, +} + +#[cfg(test)] +mod tests { + use super::*; +} + diff --git a/src/encode.rs b/src/encode.rs new file mode 100644 index 0000000..fa02faf --- /dev/null +++ b/src/encode.rs @@ -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 for Error { + fn from(value: io::Error) -> Self { + Self::Io(value) + } +} + +impl From 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 { + reader: R, + writer: W, + alphabet: B64Alphabet, +} + +impl Encoder { + pub fn new(reader: R, writer: W, alphabet: B64Alphabet) -> Self { + Self { reader, writer, alphabet } + } + + pub fn encode(&mut self) -> Result<(), Error> { + todo!() + } +} + +impl Encoder { + 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=="); + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..6e3dc9e --- /dev/null +++ b/src/lib.rs @@ -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 { + for (idx, x) in self.items.iter().enumerate() { + if *x == c { + return Some(idx) + } + }; + None + } + + pub fn pad(&self) -> char { + self.pad + } +}