201 lines
6.2 KiB
Rust
201 lines
6.2 KiB
Rust
use std::{error::Error, fmt, io::{self, ErrorKind, Read, Write}, str};
|
|
|
|
#[derive(Default, Clone, Copy)]
|
|
pub enum Style {
|
|
#[default]
|
|
Plain,
|
|
Spaces,
|
|
Prefixed,
|
|
Table,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct ParseStyleError;
|
|
|
|
impl fmt::Display for ParseStyleError {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "{self:?}")
|
|
}
|
|
}
|
|
|
|
impl Error for ParseStyleError {}
|
|
|
|
impl str::FromStr for Style {
|
|
type Err = ParseStyleError;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
match s {
|
|
"plain" => Ok(Self::Plain),
|
|
"spaces" => Ok(Self::Spaces),
|
|
"prefixed" => Ok(Self::Prefixed),
|
|
"table" => Ok(Self::Table),
|
|
_ => Err(ParseStyleError),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct Encoder<R: Read, W: Write> {
|
|
reader: R,
|
|
writer: W,
|
|
style: Style,
|
|
wrap: Option<usize>,
|
|
}
|
|
|
|
impl<R: Read, W: Write> Encoder<R, W> {
|
|
pub fn new(reader: R, writer: W, style: Option<Style>, mut wrap: Option<usize>) -> Self {
|
|
let style = style.unwrap_or_default();
|
|
if let Style::Table = style {
|
|
wrap = match wrap {
|
|
Some(u) => Some(u),
|
|
None => Some(5),
|
|
};
|
|
}
|
|
Self {
|
|
reader,
|
|
writer,
|
|
style,
|
|
wrap,
|
|
}
|
|
}
|
|
|
|
pub fn encode(mut self) -> Result<W, io::Error> {
|
|
let mut bytes = self.reader.bytes();
|
|
let mut total = 0;
|
|
loop {
|
|
match bytes.next() {
|
|
Some(Ok(b)) => match self.style {
|
|
Style::Plain => {
|
|
if let Some(w) = self.wrap {
|
|
if total > 0 && total % w == 0 {
|
|
writeln!(self.writer)?;
|
|
}
|
|
}
|
|
write!(self.writer, "{b:08b}")?;
|
|
}
|
|
Style::Spaces => {
|
|
if let Some(w) = self.wrap {
|
|
if total > 0 && total % w == 0 {
|
|
writeln!(self.writer)?;
|
|
} else {
|
|
write!(self.writer, " ")?;
|
|
}
|
|
} else if total > 0 {
|
|
write!(self.writer, " ")?;
|
|
}
|
|
write!(self.writer, "{b:08b}")?;
|
|
}
|
|
Style::Prefixed => {
|
|
if let Some(w) = self.wrap {
|
|
if total > 0 && total % w == 0 {
|
|
writeln!(self.writer)?;
|
|
} else if total > 0 {
|
|
write!(self.writer, " ")?;
|
|
}
|
|
} else if total > 0 {
|
|
write!(self.writer, " ")?;
|
|
}
|
|
write!(self.writer, "{b:#010b}")?;
|
|
}
|
|
Style::Table => {
|
|
if let Some(w) = self.wrap {
|
|
if total % w == 0 {
|
|
if total > 0 {
|
|
write!(self.writer, "\n{total:>5}\t{b:#010b}")?;
|
|
} else {
|
|
write!(self.writer, "{total:>5}\t{b:#010b}")?;
|
|
}
|
|
} else {
|
|
write!(self.writer, " {b:#010b}")?;
|
|
}
|
|
} else {
|
|
unreachable!();
|
|
}
|
|
}
|
|
},
|
|
Some(Err(e)) if e.kind() == ErrorKind::UnexpectedEof => break,
|
|
Some(Err(e)) if e.kind() == ErrorKind::Interrupted => continue,
|
|
Some(Err(e)) => return Err(e),
|
|
None => break,
|
|
}
|
|
total += 1;
|
|
}
|
|
if let Some(w) = self.wrap {
|
|
if total % w != 0 {
|
|
writeln!(self.writer)?;
|
|
}
|
|
} else {
|
|
writeln!(self.writer)?;
|
|
}
|
|
Ok(self.writer)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn hello_plain() {
|
|
let encoder = Encoder::new("Hello, World!".as_bytes(), vec![], None, None);
|
|
assert_eq!(
|
|
encoder.encode().unwrap(),
|
|
b"01001000011001010110110001101100011011110010110000100000010101110110111101110010011011000110010000100001\n"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn hello_spaced() {
|
|
let encoder = Encoder::new(
|
|
"Hello, World!".as_bytes(),
|
|
vec![],
|
|
Some(Style::Spaces),
|
|
None,
|
|
);
|
|
assert_eq!(
|
|
encoder.encode().unwrap(),
|
|
b"01001000 01100101 01101100 01101100 01101111 00101100 00100000 01010111 01101111 01110010 01101100 01100100 00100001\n"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn hello_prefixed() {
|
|
let encoder = Encoder::new(
|
|
"Hello, World!".as_bytes(),
|
|
vec![],
|
|
Some(Style::Prefixed),
|
|
None,
|
|
);
|
|
assert_eq!(
|
|
encoder.encode().unwrap(),
|
|
b"0b01001000 0b01100101 0b01101100 0b01101100 0b01101111 0b00101100 0b00100000 0b01010111 0b01101111 0b01110010 0b01101100 0b01100100 0b00100001\n"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn hello_prefixed_wrapped() {
|
|
let encoder = Encoder::new(
|
|
"Hello, World!".as_bytes(),
|
|
vec![],
|
|
Some(Style::Prefixed),
|
|
Some(4),
|
|
);
|
|
assert_eq!(
|
|
encoder.encode().unwrap(),
|
|
b"0b01001000 0b01100101 0b01101100 0b01101100\n0b01101111 0b00101100 0b00100000 0b01010111\n0b01101111 0b01110010 0b01101100 0b01100100\n0b00100001\n"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn hello_table() {
|
|
let encoder = Encoder::new(
|
|
"Hello, World!".as_bytes(),
|
|
vec![],
|
|
Some(Style::Table),
|
|
Some(4),
|
|
);
|
|
assert_eq!(
|
|
encoder.encode().unwrap(),
|
|
b" 0\t0b01001000 0b01100101 0b01101100 0b01101100\n 4\t0b01101111 0b00101100 0b00100000 0b01010111\n 8\t0b01101111 0b01110010 0b01101100 0b01100100\n 12\t0b00100001\n"
|
|
);
|
|
}
|
|
}
|