diff --git a/syft/format/table/encoder.go b/syft/format/table/encoder.go index f7dba75fd..b025acdc5 100644 --- a/syft/format/table/encoder.go +++ b/syft/format/table/encoder.go @@ -6,6 +6,7 @@ import ( "sort" "strings" + "github.com/charmbracelet/lipgloss" "github.com/olekukonko/tablewriter" "github.com/anchore/syft/syft/sbom" @@ -61,7 +62,9 @@ func (e encoder) Encode(writer io.Writer, s sbom.SBOM) error { } return false }) - rows = removeDuplicateRows(rows) + + columns = append(columns, "") // add a column for duplicate annotations + rows = markDuplicateRows(rows) table := tablewriter.NewWriter(writer) @@ -84,19 +87,38 @@ func (e encoder) Encode(writer io.Writer, s sbom.SBOM) error { return nil } -func removeDuplicateRows(items [][]string) [][]string { - seen := map[string][]string{} +func markDuplicateRows(items [][]string) [][]string { + seen := map[string]int{} var result [][]string for _, v := range items { key := strings.Join(v, "|") - if seen[key] != nil { + if _, ok := seen[key]; ok { // dup! + seen[key]++ continue } - seen[key] = v + seen[key] = 1 result = append(result, v) } + + style := lipgloss.NewStyle().Foreground(lipgloss.Color("#777777")) + for i, v := range result { + key := strings.Join(v, "|") + // var name string + var annotation string + switch seen[key] { + case 0, 1: + case 2: + annotation = "(+1 duplicate)" + default: + annotation = fmt.Sprintf("(+%d duplicates)", seen[key]-1) + } + + annotation = style.Render(annotation) + result[i] = append(v, annotation) + } + return result } diff --git a/syft/format/table/encoder_test.go b/syft/format/table/encoder_test.go index fe55f50ae..ab6126322 100644 --- a/syft/format/table/encoder_test.go +++ b/syft/format/table/encoder_test.go @@ -23,7 +23,7 @@ func TestTableEncoder(t *testing.T) { ) } -func TestRemoveDuplicateRows(t *testing.T) { +func Test_markDuplicateRows(t *testing.T) { data := [][]string{ {"1", "2", "3"}, {"a", "b", "c"}, @@ -35,13 +35,13 @@ func TestRemoveDuplicateRows(t *testing.T) { } expected := [][]string{ - {"1", "2", "3"}, - {"a", "b", "c"}, - {"4", "5", "6"}, - {"1", "2", "1"}, + {"1", "2", "3", "(+2 duplicates)"}, + {"a", "b", "c", "(+1 duplicate)"}, + {"4", "5", "6", ""}, + {"1", "2", "1", ""}, } - actual := removeDuplicateRows(data) + actual := markDuplicateRows(data) if diffs := deep.Equal(expected, actual); len(diffs) > 0 { t.Errorf("found diffs!") diff --git a/syft/format/table/test-fixtures/snapshot/TestTableEncoder.golden b/syft/format/table/test-fixtures/snapshot/TestTableEncoder.golden index 2094e69e0..f5ca61493 100644 --- a/syft/format/table/test-fixtures/snapshot/TestTableEncoder.golden +++ b/syft/format/table/test-fixtures/snapshot/TestTableEncoder.golden @@ -1,3 +1,3 @@ -NAME VERSION TYPE -package-1 1.0.1 python -package-2 2.0.1 deb +NAME VERSION TYPE +package-1 1.0.1 python +package-2 2.0.1 deb