pub use { super::B64Alphabet, std::{ fmt::{self, Write}, io::{self, ErrorKind, Read}, }, }; #[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: Option) -> Self { Self { reader, writer, alphabet: alphabet.unwrap_or_default(), } } pub fn encode(&mut self) -> Result<(), Error> { loop { let mut ibuf = [0; 3]; let mut obuf = [self.alphabet.pad(); 4]; let mut n_bytes = 0; let mut num: u64 = 0; loop { n_bytes += match self.reader.read(&mut ibuf) { Ok(n) => n, Err(e) if e.kind() == ErrorKind::Interrupted => continue, Err(e) => return Err(e.into()), }; break; } if n_bytes == 0 { break; } for (idx, n) in ibuf.iter().enumerate() { num <<= 8; if idx < n_bytes { num |= *n as u64; } } let mut outlen = n_bytes * 8 / 6; if n_bytes * 8 % 6 > 0 { outlen += 1; } for idx in (0..4).rev() { if outlen == 4 || idx < outlen { let b = num & 0b111111; obuf[idx] = self.alphabet.items[b as usize]; } num >>= 6; } write!( self.writer, "{}{}{}{}", char::try_from(obuf[0]).unwrap(), char::try_from(obuf[1]).unwrap(), char::try_from(obuf[2]).unwrap(), char::try_from(obuf[3]).unwrap() )?; if outlen < 4 { break; } } Ok(()) } } impl Encoder { pub fn output(self) -> String { self.writer } } #[cfg(test)] mod tests { use super::*; #[test] fn encode() { let mut encoder = Encoder::new("Hello, World".as_bytes(), String::new(), None); encoder.encode().unwrap(); assert_eq!(encoder.output(), "SGVsbG8sIFdvcmxk"); encoder = Encoder { reader: "Hello, World!".as_bytes(), writer: String::new(), alphabet: B64Alphabet::default(), }; encoder.encode().unwrap(); assert_eq!(encoder.output(), "SGVsbG8sIFdvcmxkIQ=="); encoder = Encoder::new("Hello, World!\n".as_bytes(), String::new(), None); encoder.encode().unwrap(); assert_eq!(encoder.output(), "SGVsbG8sIFdvcmxkIQo="); } }