diff --git a/cmd/printer/table.go b/cmd/printer/table.go new file mode 100644 index 00000000..95d7b51e --- /dev/null +++ b/cmd/printer/table.go @@ -0,0 +1,116 @@ +package printer + +import ( + "github.com/iancoleman/strcase" + "github.com/jedib0t/go-pretty/v6/table" + "github.com/jedib0t/go-pretty/v6/text" + "github.com/siyul-park/uniflow/pkg/primitive" + "github.com/xiatechs/jsonata-go" +) + +type ( + TableColumnDefinition struct { + Name string + Format string + } + + TablePrinter struct { + names []string + formats []*jsonata.Expr + } +) + +var ( + style = table.Style{ + Name: "StyleDefault", + Box: table.BoxStyle{ + BottomLeft: "", + BottomRight: "", + BottomSeparator: "", + EmptySeparator: text.RepeatAndTrim(" ", text.RuneWidthWithoutEscSequences(" ")), + Left: "", + LeftSeparator: "", + MiddleHorizontal: "", + MiddleSeparator: "", + MiddleVertical: "", + PaddingLeft: " ", + PaddingRight: " ", + PageSeparator: "\n", + Right: "", + RightSeparator: "", + TopLeft: "", + TopRight: "", + TopSeparator: "", + UnfinishedRow: " ~", + }, + Color: table.ColorOptionsDefault, + Format: table.FormatOptionsDefault, + HTML: table.DefaultHTMLOptions, + Options: table.Options{ + DrawBorder: true, + SeparateColumns: true, + SeparateFooter: false, + SeparateHeader: false, + SeparateRows: false, + }, + Title: table.TitleOptionsDefault, + } +) + +func NewTable(columns []TableColumnDefinition) (*TablePrinter, error) { + names := make([]string, len(columns)) + formats := make([]*jsonata.Expr, len(columns)) + + for i, column := range columns { + name := strcase.ToScreamingSnake(column.Name) + format, err := jsonata.Compile(column.Format) + if err != nil { + return nil, err + } + + names[i] = name + formats[i] = format + } + + return &TablePrinter{ + names: names, + formats: formats, + }, nil +} + +func (p *TablePrinter) Print(data any) (string, error) { + value, err := primitive.MarshalText(data) + if err != nil { + return "", err + } + values, ok := value.(*primitive.Slice) + if !ok { + return "", nil + } + elements := values.Slice() + + header := make(table.Row, len(p.names)) + for i, name := range p.names { + header[i] = name + } + + rows := make([]table.Row, len(elements)) + for i, element := range elements { + row := make(table.Row, len(p.formats)) + for j, format := range p.formats { + if data, err := format.Eval(element); err != nil { + row[j] = nil + } else { + row[j] = data + } + } + rows[i] = row + } + + tb := table.NewWriter() + tb.SetStyle(style) + tb.AppendHeader(header) + tb.AppendRows(rows) + + return tb.Render(), nil +} diff --git a/cmd/printer/table_test.go b/cmd/printer/table_test.go new file mode 100644 index 00000000..9177f040 --- /dev/null +++ b/cmd/printer/table_test.go @@ -0,0 +1,34 @@ +package printer + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestTablePrinter_Print(t *testing.T) { + data := []map[string]any{ + { + "foo": "foo", + "bar": "bar", + }, + } + + p, err := NewTable([]TableColumnDefinition{ + { + Name: "foo", + Format: "$.foo", + }, + { + Name: "bar", + Format: "$.bar", + }, + }) + assert.NoError(t, err) + + expect := " \n FOO BAR \n foo bar \n " + + table, err := p.Print(data) + assert.NoError(t, err) + assert.Equal(t, expect, table) +} diff --git a/go.mod b/go.mod index bf6609d7..1c4133eb 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/evanw/esbuild v0.19.5 github.com/go-faker/faker/v4 v4.2.0 github.com/iancoleman/strcase v0.3.0 + github.com/jedib0t/go-pretty/v6 v6.4.9 github.com/lithammer/dedent v1.1.0 github.com/mitchellh/hashstructure/v2 v2.0.2 github.com/oklog/ulid/v2 v2.1.0 @@ -34,10 +35,12 @@ require ( github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/klauspost/compress v1.17.0 // indirect github.com/magiconair/properties v1.8.7 // indirect + github.com/mattn/go-runewidth v0.0.13 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/montanaflynn/stats v0.7.1 // indirect github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/rivo/uniseg v0.2.0 // indirect github.com/sagikazarmark/locafero v0.3.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/shopspring/decimal v1.3.1 // indirect diff --git a/go.sum b/go.sum index fc2e1990..9434a454 100644 --- a/go.sum +++ b/go.sum @@ -162,6 +162,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1: github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jedib0t/go-pretty/v6 v6.4.9 h1:vZ6bjGg2eBSrJn365qlxGcaWu09Id+LHtrfDWlB2Usc= +github.com/jedib0t/go-pretty/v6 v6.4.9/go.mod h1:Ndk3ase2CkQbXLLNf5QDHoYb6J9WtVfmHZu9n8rk2xs= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= @@ -182,6 +184,8 @@ github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffkt github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= @@ -198,11 +202,14 @@ github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1H github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= @@ -233,6 +240,7 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= @@ -409,6 +417,7 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=