use std::io::{Write, Error};
use encode_unicode::Utf8Char;
use super::utils::NEWLINE;
#[derive(Clone, Debug, PartialEq, Copy, Hash, Eq)]
pub enum Alignment {
LEFT,
CENTER,
RIGHT,
}
#[derive(Clone, Debug, PartialEq, Copy, Hash, Eq)]
pub enum LinePosition {
Top,
Title,
Intern,
Bottom,
}
#[derive(Clone, Debug, PartialEq, Copy, Hash, Eq)]
pub enum ColumnPosition {
Left,
Intern,
Right,
}
#[derive(Clone, Debug, Copy, Hash, PartialEq, Eq)]
pub struct LineSeparator {
line: char,
junc: char,
ljunc: char,
rjunc: char,
}
impl LineSeparator {
pub fn new(line: char, junc: char, ljunc: char, rjunc: char) -> LineSeparator {
LineSeparator {
line,
junc,
ljunc,
rjunc,
}
}
fn print<T: Write + ?Sized>(&self,
out: &mut T,
col_width: &[usize],
padding: (usize, usize),
colsep: bool,
lborder: bool,
rborder: bool)
-> Result<usize, Error> {
if lborder {
out.write_all(Utf8Char::from(self.ljunc).as_bytes())?;
}
let mut iter = col_width.iter().peekable();
while let Some(width) = iter.next() {
for _ in 0..width + padding.0 + padding.1 {
out.write_all(Utf8Char::from(self.line).as_bytes())?;
}
if colsep && iter.peek().is_some() {
out.write_all(Utf8Char::from(self.junc).as_bytes())?;
}
}
if rborder {
out.write_all(Utf8Char::from(self.rjunc).as_bytes())?;
}
out.write_all(NEWLINE)?;
Ok(1)
}
}
impl Default for LineSeparator {
fn default() -> Self {
LineSeparator::new('-', '+', '+', '+')
}
}
#[derive(Clone, Debug, Copy, Hash, PartialEq, Eq)]
pub struct TableFormat {
csep: Option<char>,
lborder: Option<char>,
rborder: Option<char>,
lsep: Option<LineSeparator>,
tsep: Option<LineSeparator>,
top_sep: Option<LineSeparator>,
bottom_sep: Option<LineSeparator>,
pad_left: usize,
pad_right: usize,
indent: usize,
}
impl TableFormat {
pub fn new() -> TableFormat {
TableFormat {
csep: None,
lborder: None,
rborder: None,
lsep: None,
tsep: None,
top_sep: None,
bottom_sep: None,
pad_left: 0,
pad_right: 0,
indent: 0,
}
}
pub fn get_padding(&self) -> (usize, usize) {
(self.pad_left, self.pad_right)
}
pub fn padding(&mut self, left: usize, right: usize) {
self.pad_left = left;
self.pad_right = right;
}
pub fn column_separator(&mut self, separator: char) {
self.csep = Some(separator);
}
pub fn borders(&mut self, border: char) {
self.lborder = Some(border);
self.rborder = Some(border);
}
pub fn left_border(&mut self, border: char) {
self.lborder = Some(border);
}
pub fn right_border(&mut self, border: char) {
self.rborder = Some(border);
}
pub fn separator(&mut self, what: LinePosition, separator: LineSeparator) {
*match what {
LinePosition::Top => &mut self.top_sep,
LinePosition::Bottom => &mut self.bottom_sep,
LinePosition::Title => &mut self.tsep,
LinePosition::Intern => &mut self.lsep,
} = Some(separator);
}
pub fn separators(&mut self, what: &[LinePosition], separator: LineSeparator) {
for pos in what {
self.separator(*pos, separator);
}
}
fn get_sep_for_line(&self, pos: LinePosition) -> &Option<LineSeparator> {
match pos {
LinePosition::Intern => &self.lsep,
LinePosition::Top => &self.top_sep,
LinePosition::Bottom => &self.bottom_sep,
LinePosition::Title => {
match &self.tsep {
s @ &Some(_) => s,
&None => &self.lsep,
}
}
}
}
pub fn indent(&mut self, spaces: usize) {
self.indent = spaces;
}
pub fn get_indent(&self) -> usize {
self.indent
}
pub (crate) fn print_line_separator<T: Write + ?Sized>(&self,
out: &mut T,
col_width: &[usize],
pos: LinePosition)
-> Result<usize, Error> {
match *self.get_sep_for_line(pos) {
Some(ref l) => {
out.write_all(&vec![b' '; self.get_indent()])?;
l.print(out,
col_width,
self.get_padding(),
self.csep.is_some(),
self.lborder.is_some(),
self.rborder.is_some())
}
None => Ok(0),
}
}
pub fn get_column_separator(&self, pos: ColumnPosition) -> Option<char> {
match pos {
ColumnPosition::Left => self.lborder,
ColumnPosition::Intern => self.csep,
ColumnPosition::Right => self.rborder,
}
}
pub (crate) fn print_column_separator<T: Write + ?Sized>(&self,
out: &mut T,
pos: ColumnPosition)
-> Result<(), Error> {
match self.get_column_separator(pos) {
Some(s) => out.write_all(Utf8Char::from(s).as_bytes()),
None => Ok(()),
}
}
}
impl Default for TableFormat {
fn default() -> Self {
TableFormat::new()
}
}
pub struct FormatBuilder {
format: Box<TableFormat>,
}
impl FormatBuilder {
pub fn new() -> FormatBuilder {
FormatBuilder { format: Box::new(TableFormat::new()) }
}
pub fn padding(mut self, left: usize, right: usize) -> Self {
self.format.padding(left, right);
self
}
pub fn column_separator(mut self, separator: char) -> Self {
self.format.column_separator(separator);
self
}
pub fn borders(mut self, border: char) -> Self {
self.format.borders(border);
self
}
pub fn left_border(mut self, border: char) -> Self {
self.format.left_border(border);
self
}
pub fn right_border(mut self, border: char) -> Self {
self.format.right_border(border);
self
}
pub fn separator(mut self, what: LinePosition, separator: LineSeparator) -> Self {
self.format.separator(what, separator);
self
}
pub fn separators(mut self, what: &[LinePosition], separator: LineSeparator) -> Self {
self.format.separators(what, separator);
self
}
pub fn indent(mut self, spaces: usize) -> Self {
self.format.indent(spaces);
self
}
pub fn build(&self) -> TableFormat {
*self.format
}
}
impl Into<TableFormat> for FormatBuilder {
fn into(self) -> TableFormat {
*self.format
}
}
impl From<TableFormat> for FormatBuilder {
fn from(fmt: TableFormat) -> Self {
FormatBuilder { format: Box::new(fmt) }
}
}
pub mod consts {
use super::{TableFormat, LineSeparator, FormatBuilder, LinePosition};
lazy_static! {
static ref MINUS_PLUS_SEP: LineSeparator = LineSeparator::new('-', '+', '+', '+');
static ref EQU_PLUS_SEP: LineSeparator = LineSeparator::new('=', '+', '+', '+');
pub static ref FORMAT_DEFAULT: TableFormat = FormatBuilder::new()
.column_separator('|')
.borders('|')
.separator(LinePosition::Intern, *MINUS_PLUS_SEP)
.separator(LinePosition::Title, *EQU_PLUS_SEP)
.separator(LinePosition::Bottom, *MINUS_PLUS_SEP)
.separator(LinePosition::Top, *MINUS_PLUS_SEP)
.padding(1, 1)
.build();
pub static ref FORMAT_NO_TITLE: TableFormat = FormatBuilder::new()
.column_separator('|')
.borders('|')
.separator(LinePosition::Intern, *MINUS_PLUS_SEP)
.separator(LinePosition::Title, *MINUS_PLUS_SEP)
.separator(LinePosition::Bottom, *MINUS_PLUS_SEP)
.separator(LinePosition::Top, *MINUS_PLUS_SEP)
.padding(1, 1)
.build();
pub static ref FORMAT_NO_LINESEP_WITH_TITLE: TableFormat = FormatBuilder::new()
.column_separator('|')
.borders('|')
.separator(LinePosition::Title, *MINUS_PLUS_SEP)
.separator(LinePosition::Bottom, *MINUS_PLUS_SEP)
.separator(LinePosition::Top, *MINUS_PLUS_SEP)
.padding(1, 1)
.build();
pub static ref FORMAT_NO_LINESEP: TableFormat = FormatBuilder::new()
.column_separator('|')
.borders('|')
.separator(LinePosition::Bottom, *MINUS_PLUS_SEP)
.separator(LinePosition::Top, *MINUS_PLUS_SEP)
.padding(1, 1)
.build();
pub static ref FORMAT_NO_COLSEP: TableFormat = FormatBuilder::new()
.separator(LinePosition::Intern, *MINUS_PLUS_SEP)
.separator(LinePosition::Title, *EQU_PLUS_SEP)
.separator(LinePosition::Bottom, *MINUS_PLUS_SEP)
.separator(LinePosition::Top, *MINUS_PLUS_SEP)
.padding(1, 1)
.build();
pub static ref FORMAT_CLEAN: TableFormat = FormatBuilder::new()
.padding(1, 1)
.build();
pub static ref FORMAT_BORDERS_ONLY: TableFormat = FormatBuilder::new()
.padding(1, 1)
.separator(LinePosition::Title, *EQU_PLUS_SEP)
.separator(LinePosition::Bottom, *MINUS_PLUS_SEP)
.separator(LinePosition::Top, *MINUS_PLUS_SEP)
.borders('|')
.build();
pub static ref FORMAT_NO_BORDER: TableFormat = FormatBuilder::new()
.padding(1, 1)
.separator(LinePosition::Intern, *MINUS_PLUS_SEP)
.separator(LinePosition::Title, *EQU_PLUS_SEP)
.column_separator('|')
.build();
pub static ref FORMAT_NO_BORDER_LINE_SEPARATOR: TableFormat = FormatBuilder::new()
.padding(1, 1)
.separator(LinePosition::Title, *MINUS_PLUS_SEP)
.column_separator('|')
.build();
pub static ref FORMAT_BOX_CHARS: TableFormat = FormatBuilder::new()
.column_separator('│')
.borders('│')
.separators(&[LinePosition::Top],
LineSeparator::new('─',
'┬',
'┌',
'┐'))
.separators(&[LinePosition::Intern],
LineSeparator::new('─',
'┼',
'├',
'┤'))
.separators(&[LinePosition::Bottom],
LineSeparator::new('─',
'┴',
'└',
'┘'))
.padding(1, 1)
.build();
}
}