#![feature(core, io, unicode)]
use std::cmp;
use std::old_io as io;
use std::iter::{AdditiveIterator, range, repeat};
use std::mem;
use std::str;
#[cfg(test)]
mod test;
pub struct TabWriter<W> {
w: W,
buf: io::MemWriter,
lines: Vec<Vec<Cell>>,
curcell: Cell,
minwidth: usize,
padding: usize,
}
#[derive(Debug)]
struct Cell {
start: usize, width: usize, size: usize, }
impl<W: Writer> TabWriter<W> {
pub fn new(w: W) -> TabWriter<W> {
TabWriter {
w: w,
buf: io::MemWriter::with_capacity(1024),
lines: vec!(vec!()),
curcell: Cell::new(0),
minwidth: 2,
padding: 2,
}
}
pub fn minwidth(mut self, minwidth: usize) -> TabWriter<W> {
self.minwidth = minwidth;
self
}
pub fn padding(mut self, padding: usize) -> TabWriter<W> {
self.padding = padding;
self
}
pub fn unwrap(self) -> W {
self.w
}
fn reset(&mut self) {
self.buf = io::MemWriter::with_capacity(1024);
self.lines = vec!(vec!());
self.curcell = Cell::new(0);
}
fn add_bytes(&mut self, bytes: &[u8]) {
self.curcell.size += bytes.len();
let _ = self.buf.write_all(bytes); }
fn term_curcell(&mut self) {
let mut curcell = Cell::new(self.buf.get_ref().len());
mem::swap(&mut self.curcell, &mut curcell);
curcell.update_width(self.buf.get_ref());
self.curline_mut().push(curcell);
}
fn curline(&mut self) -> &[Cell] {
let i = self.lines.len() - 1;
&*self.lines[i]
}
fn curline_mut(&mut self) -> &mut Vec<Cell> {
let i = self.lines.len() - 1;
&mut self.lines[i]
}
}
impl Cell {
fn new(start: usize) -> Cell {
Cell { start: start, width: 0, size: 0 }
}
fn update_width(&mut self, buf: &[u8]) {
let end = self.start + self.size;
self.width = display_columns(&buf[self.start..end]);
}
}
impl<W: Writer> Writer for TabWriter<W> {
fn write_all(&mut self, buf: &[u8]) -> io::IoResult<()> {
let mut lastterm = 0us;
for (i, &c) in buf.iter().enumerate() {
match c {
b'\t' | b'\n' => {
self.add_bytes(&buf[lastterm..i]);
self.term_curcell();
lastterm = i + 1;
if c == b'\n' {
let ncells = self.curline().len();
self.lines.push(vec!());
if ncells == 1 {
try!(self.flush());
}
}
}
_ => {}
}
}
self.add_bytes(&buf[lastterm..]);
Ok(())
}
fn flush(&mut self) -> io::IoResult<()> {
if self.curcell.size > 0 {
self.term_curcell();
}
let widths = cell_widths(&self.lines, self.minwidth);
let biggest_width = widths.iter()
.map(|ws| ws.iter().map(|&w|w).max()
.unwrap_or(0))
.max().unwrap_or(0);
let padding: String =
repeat(' ').take(biggest_width + self.padding).collect();
let mut first = true;
for (line, widths) in self.lines.iter().zip(widths.iter()) {
if !first { try!(self.w.write_all(b"\n")); } else { first = false }
for (i, cell) in line.iter().enumerate() {
let bytes =
&self.buf.get_ref()[cell.start..cell.start + cell.size];
try!(self.w.write_all(bytes));
if i >= widths.len() {
assert_eq!(i, line.len()-1);
} else {
assert!(widths[i] >= cell.width);
let padsize = self.padding + widths[i] - cell.width;
try!(self.w.write_str(&padding[0..padsize]));
}
}
}
self.reset();
Ok(())
}
}
fn cell_widths(lines: &Vec<Vec<Cell>>, minwidth: usize) -> Vec<Vec<usize>> {
let mut ws: Vec<_> = range(0, lines.len()).map(|_| vec![]).collect();
for (i, iline) in lines.iter().enumerate() {
if iline.is_empty() {
continue
}
for col in range(ws[i].len(), iline.len()-1) {
let mut width = minwidth;
let mut contig_count = 0;
for line in lines[i..].iter() {
if col + 1 >= line.len() { break
}
contig_count += 1;
width = cmp::max(width, line[col].width);
}
assert!(contig_count >= 1);
for j in range(i, i+contig_count) {
ws[j].push(width);
}
}
}
ws
}
fn display_columns(bytes: &[u8]) -> usize {
match str::from_utf8(bytes) {
Err(_) => bytes.len(),
Ok(s) => s.chars().map(|c| c.width(false).unwrap_or(0)).sum(),
}
}