diff --git a/cmd/printer/table.go b/cmd/printer/table.go new file mode 100644 index 00000000..c9ab02db --- /dev/null +++ b/cmd/printer/table.go @@ -0,0 +1,114 @@ +package printer + +import ( + "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: false, + 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 := 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 + } + + var elements []any + if v, ok := value.(*primitive.Slice); ok { + elements = v.Slice() + } else if v, ok := value.(*primitive.Map); ok { + elements = append(elements, v.Interface()) + } + + 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 { + data, _ := format.Eval(element) + 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..625a843b --- /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 := " FOO BAR \n foo bar " + + table, err := p.Print(data) + assert.NoError(t, err) + assert.Equal(t, expect, table) +} diff --git a/cmd/uniflow/apply/cmd.go b/cmd/uniflow/apply/cmd.go index fbcd2c90..933ba934 100644 --- a/cmd/uniflow/apply/cmd.go +++ b/cmd/uniflow/apply/cmd.go @@ -1,11 +1,13 @@ package apply import ( + "fmt" "io/fs" "github.com/oklog/ulid/v2" "github.com/samber/lo" "github.com/siyul-park/uniflow/cmd/flag" + "github.com/siyul-park/uniflow/cmd/printer" "github.com/siyul-park/uniflow/cmd/resource" "github.com/siyul-park/uniflow/pkg/database" "github.com/siyul-park/uniflow/pkg/scheme" @@ -21,6 +23,31 @@ type ( } ) +var ( + SpecTableColumnDefinitions = []printer.TableColumnDefinition{ + { + Name: "id", + Format: "$.id", + }, + { + Name: "kind", + Format: "$.kind", + }, + { + Name: "name", + Format: "$.name", + }, + { + Name: "namespace", + Format: "$.namespace", + }, + { + Name: "links", + Format: "$.links", + }, + } +) + func NewCmd(config Config) *cobra.Command { sc := config.Scheme db := config.Database @@ -63,11 +90,10 @@ func NewCmd(config Config) *cobra.Command { for _, spec := range specs { if spec.GetID() == (ulid.ULID{}) { if spec.GetName() != "" { - exist, err := st.FindOne(ctx, storage.Where[string](scheme.KeyName).EQ(spec.GetName()).And(storage.Where[string](scheme.KeyNamespace).EQ(spec.GetNamespace()))) - if err != nil { + filter := storage.Where[string](scheme.KeyName).EQ(spec.GetName()).And(storage.Where[string](scheme.KeyNamespace).EQ(spec.GetNamespace())) + if exist, err := st.FindOne(ctx, filter); err != nil { return err - } - if exist != nil { + } else if exist != nil { spec.SetID(exist.GetID()) } } else { @@ -109,6 +135,19 @@ func NewCmd(config Config) *cobra.Command { return err } + tablePrinter, err := printer.NewTable(SpecTableColumnDefinitions) + if err != nil { + return err + } + + table, err := tablePrinter.Print(specs) + if err != nil { + return err + } + if _, err := fmt.Fprint(cmd.OutOrStdout(), table); err != nil { + return err + } + return nil }, } diff --git a/go.mod b/go.mod index e7f50d8c..41ce3067 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/evanw/esbuild v0.19.7 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/oklog/ulid/v2 v2.1.0 github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 @@ -35,10 +36,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 8c949991..90696d41 100644 --- a/go.sum +++ b/go.sum @@ -164,6 +164,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= @@ -184,6 +186,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/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= @@ -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= @@ -235,6 +242,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= @@ -411,6 +419,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=