tabwriter-1.2.1/.github/workflows/ci.yml010066400017500001731000000036401360642667300164730ustar0000000000000000name: ci on: pull_request: push: branches: - master schedule: - cron: '00 01 * * *' jobs: test: name: test runs-on: ${{ matrix.os }} strategy: matrix: build: - pinned - stable - beta - nightly - macos - win-msvc - win-gnu include: - build: pinned os: ubuntu-18.04 rust: 1.34.0 - build: stable os: ubuntu-18.04 rust: stable - build: beta os: ubuntu-18.04 rust: beta - build: nightly os: ubuntu-18.04 rust: nightly - build: macos os: macos-latest rust: stable - build: win-msvc os: windows-2019 rust: stable - build: win-gnu os: windows-2019 rust: stable-x86_64-gnu steps: - name: Checkout repository uses: actions/checkout@v1 with: fetch-depth: 1 - name: Install Rust uses: actions-rs/toolchain@v1 with: toolchain: ${{ matrix.rust }} override: true profile: minimal - run: cargo build --verbose - run: cargo doc --verbose - run: cargo test --verbose - run: cargo test --verbose --features ansi_formatting - run: cargo build --verbose --manifest-path tabwriter-bin/Cargo.toml rustfmt: name: rustfmt runs-on: ubuntu-18.04 steps: - name: Checkout repository uses: actions/checkout@v1 with: fetch-depth: 1 - name: Install Rust uses: actions-rs/toolchain@v1 with: toolchain: stable profile: minimal components: rustfmt - name: Install rustfmt run: rustup component add rustfmt - name: Check formatting run: | cargo fmt -- --check - name: Check formatting of tabwriter-bin run: | cargo fmt --manifest-path tabwriter-bin/Cargo.toml -- --check tabwriter-1.2.1/.gitignore010064400017500000144000000000511274016737400137320ustar0000000000000000.*.swp tags target ctags.rust Cargo.lock tabwriter-1.2.1/COPYING010064400017500000144000000001761274016737400130050ustar0000000000000000This project is dual-licensed under the Unlicense and MIT licenses. You may use this code under the terms of either license. tabwriter-1.2.1/Cargo.toml.orig010066400017500001731000000012431360662064600146400ustar0000000000000000[package] name = "tabwriter" version = "1.2.1" #:version authors = ["Andrew Gallant "] description = "Elastic tabstops." documentation = "https://docs.rs/tabwriter" homepage = "https://github.com/BurntSushi/tabwriter" repository = "https://github.com/BurntSushi/tabwriter" readme = "README.md" keywords = ["tabs", "elastic", "aligned", "whitespace", "table"] license = "Unlicense/MIT" edition = "2018" [dependencies] unicode-width = "0.1" lazy_static = { version = "1", optional = true} [dependencies.regex] version = "1.3" optional = true default-features = false features = ["std"] [features] default = [] ansi_formatting = ["regex", "lazy_static"] tabwriter-1.2.1/Cargo.toml0000644000000022651360662064700111530ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "tabwriter" version = "1.2.1" authors = ["Andrew Gallant "] description = "Elastic tabstops." homepage = "https://github.com/BurntSushi/tabwriter" documentation = "https://docs.rs/tabwriter" readme = "README.md" keywords = ["tabs", "elastic", "aligned", "whitespace", "table"] license = "Unlicense/MIT" repository = "https://github.com/BurntSushi/tabwriter" [dependencies.lazy_static] version = "1" optional = true [dependencies.regex] version = "1.3" features = ["std"] optional = true default-features = false [dependencies.unicode-width] version = "0.1" [features] ansi_formatting = ["regex", "lazy_static"] default = [] tabwriter-1.2.1/LICENSE-MIT010064400017500000144000000020711274016737400134020ustar0000000000000000The MIT License (MIT) Copyright (c) 2015 Andrew Gallant Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. tabwriter-1.2.1/Makefile010064400017500000144000000003761274016737400134140ustar0000000000000000all: echo Nothing to do... ctags: ctags --recurse --options=ctags.rust --languages=Rust docs: cargo doc in-dir ./target/doc fix-perms rscp ./target/doc/* gopher:~/www/burntsushi.net/rustdoc/ push: git push origin master git push github master tabwriter-1.2.1/README.md010066400017500001731000000053141360642667300132370ustar0000000000000000tabwriter is a crate that implements [elastic tabstops](http://nickgravgaard.com/elastictabstops/index.html). It provides both a library for wrapping Rust `Writer`s and a small program that exposes the same functionality at the command line. [![Build status](https://github.com/BurntSushi/tabwriter/workflows/ci/badge.svg)](https://github.com/BurntSushi/tabwriter/actions) [![](http://meritbadge.herokuapp.com/tabwriter)](https://crates.io/crates/tabwriter) Dual-licensed under MIT or the [UNLICENSE](http://unlicense.org). ### Simple example of library ```rust use std::io::Write; use tabwriter::TabWriter; let mut tw = TabWriter::new(vec![]); tw.write_all(b" Bruce Springsteen\tBorn to Run Bob Seger\tNight Moves Metallica\tBlack The Boss\tDarkness on the Edge of Town ").unwrap(); tw.flush().unwrap(); let written = String::from_utf8(tw.into_inner().unwrap()).unwrap(); assert_eq!(&written, " Bruce Springsteen Born to Run Bob Seger Night Moves Metallica Black The Boss Darkness on the Edge of Town "); ``` You can see an example of *real* use in my [CSV toolkit](https://github.com/BurntSushi/xsv/blob/master/src/cmd/table.rs#L57-L60). ### Simple example of command line utility ```bash [andrew@Liger tabwriter] cat sample | sed 's/ /\\t/g' a\tb\tc abc\tmnopqrstuv\txyz abcmnoxyz\tmore text a\tb\tc [andrew@Liger tabwriter] ./target/tabwriter < sample a b c abc mnopqrstuv xyz abcmnoxyz more text a b c ``` Notice that once a column block is broken, alignment starts over again. ### Documentation The API is fully documented with some examples: [http://burntsushi.net/rustdoc/tabwriter/](http://burntsushi.net/rustdoc/tabwriter/). ### Installation This crate works with Cargo. Assuming you have Rust and [Cargo](http://crates.io/) installed, simply check out the source and run tests: ```bash git clone git://github.com/BurntSushi/tabwriter cd tabwriter cargo test ``` You can also add `tabwriter` as a dependency to your project's `Cargo.toml`: ```toml [dependencies] tabwriter = "1" ``` ### Dealing with ANSI escape codes If you want `tabwriter` to be aware of ANSI escape codes, then compile it with the `ansi_formatting` feature enabled. ### Minimum Rust version policy This crate's minimum supported `rustc` version is `1.34.0`. The current policy is that the minimum Rust version required to use this crate can be increased in minor version updates. For example, if `crate 1.0` requires Rust 1.20.0, then `crate 1.0.z` for all values of `z` will also require Rust 1.20.0 or newer. However, `crate 1.y` for `y > 0` may require a newer minimum version of Rust. In general, this crate will be conservative with respect to the minimum supported version of Rust. tabwriter-1.2.1/UNLICENSE010064400017500000144000000022731274016737400132220ustar0000000000000000This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to tabwriter-1.2.1/rustfmt.toml010066400017500001731000000000541360642667300143550ustar0000000000000000max_width = 79 use_small_heuristics = "max" tabwriter-1.2.1/session.vim010064400017500000144000000000701274016737400141430ustar0000000000000000au BufWritePost *.rs silent!make ctags > /dev/null 2>&1 tabwriter-1.2.1/src/lib.rs010066400017500001731000000321501360662064400136530ustar0000000000000000//! This crate provides an implementation of //! [elastic tabstops](http://nickgravgaard.com/elastictabstops/index.html). //! It is a minimal port of Go's //! [tabwriter](http://golang.org/pkg/text/tabwriter/) package. //! Namely, its main mode of operation is to wrap a `Writer` and implement //! elastic tabstops for the text written to the wrapped `Writer`. //! //! This package is also bundled with a program, `tabwriter`, //! that exposes this functionality at the command line. //! //! Here's an example that shows basic alignment: //! //! ```rust //! use std::io::Write; //! use tabwriter::TabWriter; //! //! let mut tw = TabWriter::new(vec![]); //! write!(&mut tw, " //! Bruce Springsteen\tBorn to Run //! Bob Seger\tNight Moves //! Metallica\tBlack //! The Boss\tDarkness on the Edge of Town //! ").unwrap(); //! tw.flush().unwrap(); //! //! let written = String::from_utf8(tw.into_inner().unwrap()).unwrap(); //! assert_eq!(&*written, " //! Bruce Springsteen Born to Run //! Bob Seger Night Moves //! Metallica Black //! The Boss Darkness on the Edge of Town //! "); //! ``` //! //! Note that `flush` **must** be called or else `TabWriter` may never write //! anything. This is because elastic tabstops requires knowing about future //! lines in order to align output. More precisely, all text considered in a //! single alignment must fit into memory. //! //! Here's another example that demonstrates how *only* contiguous columns //! are aligned: //! //! ```rust //! use std::io::Write; //! use tabwriter::TabWriter; //! //! let mut tw = TabWriter::new(vec![]).padding(1); //! write!(&mut tw, " //!fn foobar() {{ //! let mut x = 1+1;\t// addition //! x += 1;\t// increment in place //! let y = x * x * x * x;\t// multiply! //! //! y += 1;\t// this is another group //! y += 2 * 2;\t// that is separately aligned //!}} //!").unwrap(); //! tw.flush().unwrap(); //! //! let written = String::from_utf8(tw.into_inner().unwrap()).unwrap(); //! assert_eq!(&*written, " //!fn foobar() { //! let mut x = 1+1; // addition //! x += 1; // increment in place //! let y = x * x * x * x; // multiply! //! //! y += 1; // this is another group //! y += 2 * 2; // that is separately aligned //!} //!"); //! ``` #![deny(missing_docs)] use std::cmp; use std::error; use std::fmt; use std::io::{self, Write}; use std::iter; use std::mem; use std::str; #[cfg(test)] mod test; /// TabWriter wraps an arbitrary writer and aligns tabbed output. /// /// Elastic tabstops work by aligning *contiguous* tabbed delimited fields /// known as *column blocks*. When a line appears that breaks all contiguous /// blocks, all buffered output will be flushed to the underlying writer. /// Otherwise, output will stay buffered until `flush` is explicitly called. #[derive(Debug)] pub struct TabWriter { w: W, buf: io::Cursor>, lines: Vec>, curcell: Cell, minwidth: usize, padding: usize, alignment: Alignment, } /// `Alignment` represents how a `TabWriter` should align text within its cell. #[derive(Debug)] pub enum Alignment { /// Text should be aligned with the left edge of the cell Left, /// Text should be centered within the cell Center, /// Text should be aligned with the right edge of the cell Right, } #[derive(Debug)] struct Cell { start: usize, // offset into TabWriter.buf width: usize, // in characters size: usize, // in bytes } impl TabWriter { /// Create a new `TabWriter` from an existing `Writer`. /// /// All output written to `Writer` is passed through `TabWriter`. /// Contiguous column blocks indicated by tabs are aligned. /// /// Note that `flush` must be called to guarantee that `TabWriter` will /// write to the given writer. pub fn new(w: W) -> TabWriter { TabWriter { w: w, buf: io::Cursor::new(Vec::with_capacity(1024)), lines: vec![vec![]], curcell: Cell::new(0), minwidth: 2, padding: 2, alignment: Alignment::Left, } } /// Set the minimum width of each column. That is, all columns will have /// *at least* the size given here. If a column is smaller than `minwidth`, /// then it is padded with spaces. /// /// The default minimum width is `2`. pub fn minwidth(mut self, minwidth: usize) -> TabWriter { self.minwidth = minwidth; self } /// Set the padding between columns. All columns will be separated by /// *at least* the number of spaces indicated by `padding`. If `padding` /// is zero, then columns may run up against each other without any /// separation. /// /// The default padding is `2`. pub fn padding(mut self, padding: usize) -> TabWriter { self.padding = padding; self } /// Set the alignment of text within cells. This will effect future flushes. /// /// The default alignment is `Alignment::Left`. pub fn alignment(mut self, alignment: Alignment) -> TabWriter { self.alignment = alignment; self } /// Unwraps this `TabWriter`, returning the underlying writer. /// /// This internal buffer is flushed before returning the writer. If the /// flush fails, then an error is returned. pub fn into_inner(mut self) -> Result>> { match self.flush() { Ok(()) => Ok(self.w), Err(err) => Err(IntoInnerError(self, err)), } } /// Resets the state of the aligner. Once the aligner is reset, all future /// writes will start producing a new alignment. fn reset(&mut self) { self.buf = io::Cursor::new(Vec::with_capacity(1024)); self.lines = vec![vec![]]; self.curcell = Cell::new(0); } /// Adds the bytes received into the buffer and updates the size of /// the current cell. fn add_bytes(&mut self, bytes: &[u8]) { self.curcell.size += bytes.len(); let _ = self.buf.write_all(bytes); // cannot fail } /// Ends the current cell, updates the UTF8 width of the cell and starts /// a fresh cell. fn term_curcell(&mut self) { let mut curcell = Cell::new(self.buf.position() as usize); mem::swap(&mut self.curcell, &mut curcell); curcell.update_width(&self.buf.get_ref()); self.curline_mut().push(curcell); } /// Return a view of the current line of cells. fn curline(&mut self) -> &[Cell] { let i = self.lines.len() - 1; &*self.lines[i] } /// Return a mutable view of the current line of cells. fn curline_mut(&mut self) -> &mut Vec { 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 io::Write for TabWriter { fn write(&mut self, buf: &[u8]) -> io::Result { let mut lastterm = 0usize; 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![]); // Having a single cell means that *all* previous // columns have been broken, so we should just flush. if ncells == 1 { self.flush()?; } } } _ => {} } } self.add_bytes(&buf[lastterm..]); Ok(buf.len()) } fn flush(&mut self) -> io::Result<()> { if self.curcell.size > 0 { self.term_curcell(); } let widths = cell_widths(&self.lines, self.minwidth); // This is a trick to avoid allocating padding for every cell. // Just allocate the most we'll ever need and borrow from it. let biggest_width = widths .iter() .map(|ws| ws.iter().map(|&w| w).max().unwrap_or(0)) .max() .unwrap_or(0); let padding: String = iter::repeat(' ').take(biggest_width + self.padding).collect(); let mut first = true; for (line, widths) in self.lines.iter().zip(widths.iter()) { if !first { 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]; if i >= widths.len() { // There is no width for the last column assert_eq!(i, line.len() - 1); self.w.write_all(bytes)?; } else { assert!(widths[i] >= cell.width); let extra_space = widths[i] - cell.width; let (left_spaces, mut right_spaces) = match self.alignment { Alignment::Left => (0, extra_space), Alignment::Right => (extra_space, 0), Alignment::Center => { (extra_space / 2, extra_space - extra_space / 2) } }; right_spaces += self.padding; write!(&mut self.w, "{}", &padding[0..left_spaces])?; self.w.write_all(bytes)?; write!(&mut self.w, "{}", &padding[0..right_spaces])?; } } } self.reset(); Ok(()) } } /// An error returned by `into_inner`. /// /// This combines the error that happened while flushing the buffer with the /// `TabWriter` itself. pub struct IntoInnerError(W, io::Error); impl IntoInnerError { /// Returns the error which caused the `into_error()` call to fail. pub fn error(&self) -> &io::Error { &self.1 } /// Returns the `TabWriter` instance which generated the error. pub fn into_inner(self) -> W { self.0 } } impl fmt::Debug for IntoInnerError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.error().fmt(f) } } impl fmt::Display for IntoInnerError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.error().fmt(f) } } impl error::Error for IntoInnerError { fn description(&self) -> &str { self.error().description() } fn cause(&self) -> Option<&dyn error::Error> { Some(self.error()) } } fn cell_widths(lines: &Vec>, minwidth: usize) -> Vec> { // Naively, this algorithm looks like it could be O(n^2m) where `n` is // the number of lines and `m` is the number of contiguous columns. // // However, I claim that it is actually O(nm). That is, the width for // every contiguous column is computed exactly once. let mut ws: Vec<_> = (0..lines.len()).map(|_| vec![]).collect(); for (i, iline) in lines.iter().enumerate() { if iline.is_empty() { continue; } for col in 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() { // ignores last column break; } contig_count += 1; width = cmp::max(width, line[col].width); } assert!(contig_count >= 1); for j in i..(i + contig_count) { ws[j].push(width); } } } ws } #[cfg(not(feature = "ansi_formatting"))] fn display_columns(bytes: &[u8]) -> usize { use unicode_width::UnicodeWidthChar; // If we have a Unicode string, then attempt to guess the number of // *display* columns used. match str::from_utf8(bytes) { Err(_) => bytes.len(), Ok(s) => s .chars() .map(|c| UnicodeWidthChar::width(c).unwrap_or(0)) .fold(0, |sum, width| sum + width), } } #[cfg(feature = "ansi_formatting")] fn display_columns(bytes: &[u8]) -> usize { use unicode_width::UnicodeWidthChar; // If we have a Unicode string, then attempt to guess the number of // *display* columns used. match str::from_utf8(bytes) { Err(_) => bytes.len(), Ok(s) => strip_formatting(s) .chars() .map(|c| UnicodeWidthChar::width(c).unwrap_or(0)) .fold(0, |sum, width| sum + width), } } #[cfg(feature = "ansi_formatting")] fn strip_formatting<'t>(input: &'t str) -> std::borrow::Cow<'t, str> { use regex::Regex; lazy_static::lazy_static! { static ref RE: Regex = Regex::new(r#"\x1B\[.+?m"#).unwrap(); } RE.replace_all(input, "") } tabwriter-1.2.1/src/test.rs010066400017500001731000000116601360642667300140750ustar0000000000000000use crate::Alignment; use crate::TabWriter; use std::io::Write; fn ordie(r: Result) -> T { match r { Ok(r) => r, Err(e) => panic!("{}", e.to_string()), } } fn readable_str(s: &str) -> String { s.replace(" ", "·") } fn tabw() -> TabWriter> { TabWriter::new(Vec::new()) } fn tabify(mut tw: TabWriter>, s: &str) -> String { ordie(write!(&mut tw, "{}", s)); ordie(tw.flush()); ordie(String::from_utf8(tw.into_inner().unwrap())) } fn iseq(tw: TabWriter>, s: &str, expected: &str) { let written = tabify(tw, s); if expected != written { panic!( "\n\nexpected:\n-----\n{}\n-----\ngot:\n-----\n{}\n-----\n\n", readable_str(expected), readable_str(&written) ); } } #[test] fn test_no_cells() { iseq(tabw(), "foo\nbar\nfubar", "foo\nbar\nfubar"); } #[test] fn test_no_cells_trailing() { iseq(tabw(), "foo\nbar\nfubar\n", "foo\nbar\nfubar\n"); } #[test] fn test_no_cells_prior() { iseq(tabw(), "\nfoo\nbar\nfubar", "\nfoo\nbar\nfubar"); } #[test] fn test_empty() { iseq(tabw(), "", ""); } #[test] fn test_empty_lines() { iseq(tabw(), "\n\n\n\n", "\n\n\n\n"); } #[test] fn test_empty_cell() { iseq(tabw().padding(0).minwidth(2), "\t\n", " \n"); } #[test] fn test_empty_cell_no_min() { iseq(tabw().padding(0).minwidth(0), "\t\n", "\n"); } #[test] fn test_empty_cells() { iseq(tabw().padding(0).minwidth(2), "\t\t\n", " \n"); } #[test] fn test_empty_cells_no_min() { iseq(tabw().padding(0).minwidth(0), "\t\t\n", "\n"); } #[test] fn test_empty_cells_ignore_trailing() { iseq(tabw().padding(0).minwidth(2), "\t\t\t", " "); } #[test] fn test_one_cell() { iseq(tabw().padding(2).minwidth(2), "a\tb\nxx\tyy", "a b\nxx yy"); } #[test] fn test_one_cell_right() { iseq( tabw().padding(2).minwidth(2).alignment(Alignment::Right), "a\tb\nxx\tyy", " a b\nxx yy", ); } #[test] fn test_one_cell_center() { iseq( tabw().padding(2).minwidth(2).alignment(Alignment::Center), "a\tb\nxx\tyy", "a b\nxx yy", ); } #[test] fn test_no_padding() { iseq(tabw().padding(0).minwidth(2), "a\tb\nxx\tyy", "a b\nxxyy"); } // See: https://github.com/BurntSushi/tabwriter/issues/26 #[test] fn test_no_padding_one_row() { iseq(tabw().padding(0).minwidth(2), "a\tb\n", "a b\n"); iseq(tabw().padding(0).minwidth(1), "a\tb\n", "ab\n"); iseq(tabw().padding(0).minwidth(0), "a\tb\n", "ab\n"); } #[test] fn test_minwidth() { iseq(tabw().minwidth(5).padding(0), "a\tb\nxx\tyy", "a b\nxx yy"); } #[test] fn test_contiguous_columns() { iseq( tabw().padding(1).minwidth(0), "x\tfoo\tx\nx\tfoofoo\tx\n\nx\tfoofoofoo\tx", "x foo x\nx foofoo x\n\nx foofoofoo x", ); } #[test] fn test_table_right() { iseq( tabw().padding(1).minwidth(0).alignment(Alignment::Right), "x\tfoo\txx\t\nxx\tfoofoo\tx\t\n", " x foo xx \nxx foofoo x \n", ); } #[test] fn test_table_center() { iseq( tabw().padding(1).minwidth(0).alignment(Alignment::Center), "x\tfoo\txx\t\nxx\tfoofoo\tx\t\n", "x foo xx \nxx foofoo x \n", ); } #[test] fn test_contiguous_columns_right() { iseq( tabw().padding(1).minwidth(0).alignment(Alignment::Right), "x\tfoo\tx\nx\tfoofoo\tx\n\nx\tfoofoofoo\tx", "x foo x\nx foofoo x\n\nx foofoofoo x", ); } #[test] fn test_contiguous_columns_center() { iseq( tabw().padding(1).minwidth(0).alignment(Alignment::Center), "x\tfoo\tx\nx\tfoofoo\tx\n\nx\tfoofoofoo\tx", "x foo x\nx foofoo x\n\nx foofoofoo x", ); } #[test] fn test_unicode() { iseq( tabw().padding(2).minwidth(2), "a\tÞykkvibær\tz\naaaa\tïn Bou Chella\tzzzz\na\tBâb el Ahmar\tz", "a Þykkvibær z\n\ aaaa ïn Bou Chella zzzz\n\ a Bâb el Ahmar z", ) } #[test] fn test_contiguous_columns_complex() { iseq( tabw().padding(1).minwidth(3), " fn foobar() { let mut x = 1+1; // addition x += 1; // increment in place let y = x * x * x * x; // multiply! y += 1; // this is another group y += 2 * 2; // that is separately aligned } ", " fn foobar() { let mut x = 1+1; // addition x += 1; // increment in place let y = x * x * x * x; // multiply! y += 1; // this is another group y += 2 * 2; // that is separately aligned } ", ); } #[test] #[cfg(feature = "ansi_formatting")] fn test_ansi_formatting() { let output = "foo\tbar\tfoobar\n\ \x1b[31mföÅ\x1b[0m\t\x1b[32mbär\x1b[0m\t\x1b[36mfoobar\x1b[0m\n\ \x1b[34mfoo\tbar\tfoobar\n\x1b[0m"; iseq( tabw(), &output[..], "foo bar foobar\n\ \x1b[31mföÅ\x1b[0m \x1b[32mbär\x1b[0m \x1b[36mfoobar\x1b[0m\n\ \x1b[34mfoo bar foobar\n\x1b[0m", ) } tabwriter-1.2.1/.cargo_vcs_info.json0000644000000001121360662064700131420ustar00{ "git": { "sha1": "fb01bd7c802043077d2cbaa41c8e73a74bb6a895" } }