diff --git a/table.go b/table.go index 4c9f134..b792c88 100644 --- a/table.go +++ b/table.go @@ -28,14 +28,16 @@ type Table struct { // Separator is the seperator for columns in the table. Default is "\t" Separator string - mtx *sync.RWMutex + mtx *sync.RWMutex + rightAlign map[int]bool } // New returns a new Table with default values func New() *Table { return &Table{ - Separator: Separator, - mtx: new(sync.RWMutex), + Separator: Separator, + mtx: new(sync.RWMutex), + rightAlign: map[int]bool{}, } } @@ -53,6 +55,12 @@ func (t *Table) Bytes() []byte { return []byte(t.String()) } +func (t *Table) RightAlign(col int) { + t.mtx.Lock() + t.rightAlign[col] = true + t.mtx.Unlock() +} + // String returns the string value of table func (t *Table) String() string { t.mtx.RLock() @@ -87,6 +95,7 @@ func (t *Table) String() string { for i, cell := range row.Cells { cell.Width = colwidths[i] cell.Wrap = t.Wrap + cell.RightAlign = t.rightAlign[i] } lines = append(lines, row.String()) } @@ -158,6 +167,9 @@ type Cell struct { // Wrap when true wraps the contents of the cell when the lenght exceeds the width Wrap bool + // RightAlign when true aligns contents to the right + RightAlign bool + // Data is the cell data Data interface{} } @@ -180,11 +192,12 @@ func (c *Cell) String() string { return strutil.PadLeft(" ", int(c.Width), ' ') } s := fmt.Sprintf("%v", c.Data) - switch { - case c.Width > 0 && c.Wrap: - return wordwrap.WrapString(s, c.Width) - case c.Width > 0: - return strutil.Resize(s, c.Width) + if c.Width > 0 { + if c.Wrap && uint(len(s)) > c.Width { + return wordwrap.WrapString(s, c.Width) + } else { + return strutil.Resize(s, c.Width, c.RightAlign) + } } return s } diff --git a/table_test.go b/table_test.go index 5905365..f8f1ffc 100644 --- a/table_test.go +++ b/table_test.go @@ -45,6 +45,25 @@ func TestRow(t *testing.T) { } } +func TestAlign(t *testing.T) { + table := New() + table.AddRow("foo", "bar baz") + table.Rows = []*Row{{ + Separator: "\t", + Cells: []*Cell{ + {Data: "foo", Width: 5, Wrap: true}, + {Data: "bar baz", Width: 10, Wrap: true}, + }, + }} + table.RightAlign(1) + got := table.String() + need := "foo \t bar baz" + + if got != need { + t.Fatalf("need: %q | got: %q ", need, got) + } +} + func TestAddRow(t *testing.T) { var wg sync.WaitGroup table := New() diff --git a/util/strutil/strutil.go b/util/strutil/strutil.go index 7f5d8fe..cb35bce 100644 --- a/util/strutil/strutil.go +++ b/util/strutil/strutil.go @@ -35,14 +35,18 @@ func PadLeft(str string, length int, pad byte) string { // Resize resizes the string with the given length. It ellipses with '...' when the string's length exceeds // the desired length or pads spaces to the right of the string when length is smaller than desired -func Resize(s string, length uint) string { +func Resize(s string, length uint, rightAlign bool) string { slen := runewidth.StringWidth(s) n := int(length) if slen == n { return s } // Pads only when length of the string smaller than len needed - s = PadRight(s, n, ' ') + if rightAlign { + s = PadLeft(s, n, ' ') + } else { + s = PadRight(s, n, ' ') + } if slen > n { rs := []rune(s) var buf bytes.Buffer diff --git a/util/strutil/strutil_test.go b/util/strutil/strutil_test.go index a63c555..e7356d0 100644 --- a/util/strutil/strutil_test.go +++ b/util/strutil/strutil_test.go @@ -6,18 +6,30 @@ import ( func TestResize(t *testing.T) { s := "foo" - got := Resize(s, 5) + got := Resize(s, 5, false) if len(got) != 5 { t.Fatal("want", 5, "got", len(got)) } s = "foobar" - got = Resize(s, 5) + got = Resize(s, 5, false) if got != "fo..." { t.Fatal("want", "fo...", "got", got) } } +func TestAlign(t *testing.T) { + s := "foo" + got := Resize(s, 5, false) + if got != "foo " { + t.Fatal("want", "foo ", "got", got) + } + got = Resize(s, 5, true) + if got != " foo" { + t.Fatal("want", " foo", "got", got) + } +} + func TestJoin(t *testing.T) { got := Join([]string{"foo", "bar"}, ",") if got != "foo,bar" {