rorschach 0.1.3

Binary data definition and formatter.
Documentation
use std::fmt;
use std::any::{TypeId};
use std::io::{self, Read, Write};
use ansi_term;
use {Formatter, Definition, Field};
use super::Style;

/// Structured formatter, like the ones seen in network RFCs.
#[derive(Clone, Copy, PartialEq, Default, Debug)]
pub struct Structured {
	header: bool,
	style:  Option<Style>,
}

impl Structured {
	/// Whether to print the header or not.
	pub fn header(mut self, value: bool) -> Self {
		self.header = value;
		self
	}

	/// The default colors, once set it will always color the output.
	pub fn style(mut self, value: Style) -> Self {
		self.style = Some(value);
		self
	}
}

impl Formatter for Structured {
	fn format<R: Read, W: Write>(&self, def: &Definition, mut input: R, mut output: W) -> io::Result<()> {
		if self.header {
			try!(writeln!(output, " 0                   1                   2                   3"));
			try!(writeln!(output, " 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1"));
			try!(writeln!(output, "+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+"));
		}

		let mut printer = Printer { consumed: 0, padding: 0, output: output.by_ref() };

		for field in def.fields() {
			match field {
				&Field::Named(ref field) => {
					let kind  = field.kind().unwrap_or(TypeId::of::<()>());
					let style = self.style.and(field.style());

					if kind == TypeId::of::<u8>() {
						try!(printer.print(field.decode::<u8, _>(input.by_ref()).unwrap(),
							field.bits(), style));
					}
					else if kind == TypeId::of::<u16>() {
						try!(printer.print(field.decode::<u16, _>(input.by_ref()).unwrap(),
							field.bits(), style));
					}
					else if kind == TypeId::of::<u32>() {
						try!(printer.print(field.decode::<u32, _>(input.by_ref()).unwrap(),
							field.bits(), style));
					}
					else if kind == TypeId::of::<i8>() {
						try!(printer.print(field.decode::<i8, _>(input.by_ref()).unwrap(),
							field.bits(), style));
					}
					else if kind == TypeId::of::<i16>() {
						try!(printer.print(field.decode::<i16, _>(input.by_ref()).unwrap(),
							field.bits(), style));
					}
					else if kind == TypeId::of::<i32>() {
						try!(printer.print(field.decode::<i32, _>(input.by_ref()).unwrap(),
							field.bits(), style));
					}
					else if field.binary() {
						try!(printer.binary(&try!(field.read(input.by_ref())),
							field.bits(), style));
					}
					else {
						try!(printer.hex(&try!(field.read(input.by_ref())),
							field.bits(), style));
					}
				}

				&Field::Constant(ref field) => {
					let data = try!(field.read(input.by_ref()));

					if data == field.value() {
						try!(printer.hex(&data,
							field.bits(), self.style.map(|s| s.constant.0)));
					}
					else {
						try!(printer.hex(&data,
							field.bits(), self.style.map(|s| s.constant.1)));
					}
				}

				&Field::Garbage(ref field) => {
					try!(printer.hex(&try!(field.read(input.by_ref())),
						field.bits(), self.style.map(|s| s.garbage)));
				}

				&Field::Unknown(ref field) => {
					try!(printer.hex(&try!(field.read(input.by_ref())),
						field.bits(), self.style.map(|s| s.unknown)));
				}

				&Field::Padding(ref field) => {
					for byte in try!(field.read(input.by_ref())) {
						if byte != 0 {
							try!(printer.byte(byte, self.style.map(|s| s.padding.1)));
						}
						else {
							try!(printer.pad());
						}
					}
				}
			}
		}

		printer.finish()
	}
}

struct Printer<W: Write> {
	consumed: usize,
	padding:  usize,
	output:   W,
}

impl<W: Write> Printer<W> {
	fn remaining(&self) -> usize {
		32 - (self.consumed % 32)
	}

	fn space(string: &str, width: usize) -> (usize, usize) {
		let rem  = width - string.len();
		let half = if rem % 2 == 0 { rem / 2 } else { (rem - 1) / 2 };

		if rem % 2 == 0 {
			(half, half)
		}
		else {
			(half + 1, half)
		}
	}

	pub fn pad(&mut self) -> io::Result<()> {
		self.padding += 1;

		if self.padding > 1 && self.consumed % 32 != 0 {
			try!(write!(self.output, " "));
		}
		else {
			try!(write!(self.output, "|"));
		}

		try!(write!(self.output, "{: ^15}", ""));

		self.done(8)
	}

	pub fn byte(&mut self, data: u8, style: Option<ansi_term::Style>) -> io::Result<()> {
		self.hex(&[data][..], 8, style)
	}

	pub fn binary(&mut self, data: &[u8], bits: usize, style: Option<ansi_term::Style>) -> io::Result<()> {
		self.print({
			let mut out = String::with_capacity(data.len() * 8);

			for byte in data {
				out.push_str(&format!("{:08b}", byte));
			}

			out.pop();
			out
		}, bits, style)
	}

	pub fn hex(&mut self, mut data: &[u8], mut bits: usize, style: Option<ansi_term::Style>) -> io::Result<()> {
		let remaining = self.remaining();

		if bits > remaining {
			try!(self.hex(&data[.. remaining / 8], remaining, style));
			data  = &data[remaining / 8 ..];
			bits -= remaining;
		}

		if bits > 32 {
			for data in data.chunks(4) {
				try!(self.hex(data, data.len() * 8, style));
			}

			return Ok(());
		}

		self.print({
			let mut out = String::with_capacity(data.len() * 3);

			for byte in data {
				out.push_str(&format!("{:02x} ", byte));
			}

			out.pop();
			out
		}, bits, style)
	}

	pub fn print<T: fmt::Display>(&mut self, data: T, bits: usize, style: Option<ansi_term::Style>) -> io::Result<()> {
		let remaining = self.remaining();
		let string    = data.to_string();
		let width;

		if bits > remaining {
			if bits - remaining > remaining {
				for _ in 0 .. (remaining / 8) {
					try!(self.pad())
				}

				width = (bits - remaining) * 2 - 1;
			}
			else {
				width = remaining * 2 - 1;
			}
		}
		else {
			width = bits * 2 - 1;
		}

		let (left, right) = Self::space(&string, width);

		try!(write!(self.output, "|"));
		try!(write!(self.output, "{: ^1$}", "", left));

		if let Some(style) = style {
			try!(write!(self.output, "{}", style.paint(string)));
		}
		else {
			try!(write!(self.output, "{}", string));
		}

		try!(write!(self.output, "{: ^1$}", "", right));

		self.padding = 0;

		if bits > remaining {
			if bits - remaining <= remaining {
				try!(self.done((width + 1) / 2));

				for _ in 0 .. ((bits - remaining) / 8) {
					try!(self.pad());
				}
			}
			else {
				try!(self.done(bits));
			}
		}
		else {
			try!(self.done(bits));
		}

		Ok(())
	}

	pub fn finish(&mut self) -> io::Result<()> {
		if self.consumed % 32 == 0 {
			return Ok(());
		}

		try!(writeln!(self.output, "|"));

		for _ in 0 .. (self.consumed % 32) {
			try!(write!(self.output, "+-"));
		}

		try!(writeln!(self.output, "+"));

		Ok(())
	}

	fn done(&mut self, bits: usize) -> io::Result<()> {
		self.consumed += bits;

		if self.consumed % 32 == 0 {
			try!(writeln!(self.output, "|"));
			try!(writeln!(self.output, "+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+"));
		}

		Ok(())
	}
}