Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ structalign [flags] [packages]
-verbose in -inspect mode, show padding on its own `_` line
-tags preserve struct field tags in output (default: strip them)
-summary in diff mode, print a one-line summary after the diffs
-sort in diff mode, present structs largest-first (by bytes saved)
-sort present results largest-first (diff: by bytes saved;
inspect: by struct size)

-type string only consider named structs matching these comma-separated
glob patterns (e.g. "*Request,Config"); empty means all
Expand Down
16 changes: 11 additions & 5 deletions internal/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ func (a *App) Run(args []string) int {
fs.BoolVar(&opt.generated, "generated", false, "also analyze generated files (// Code generated ... DO NOT EDIT.)")
fs.BoolVar(&opt.skipCachePadded, "skip-cache-padded", false, "skip structs containing a golang.org/x/sys/cpu.CacheLinePad field")
fs.BoolVar(&opt.summary, "summary", false, "in diff mode, print a one-line summary after the diffs")
fs.BoolVar(&opt.sort, "sort", false, "present results largest-first (diff: by bytes saved)")
fs.BoolVar(&opt.sort, "sort", false, "present results largest-first (diff: by bytes saved; inspect: by struct size)")
fs.Usage = func() {
fmt.Fprintf(a.Stderr, "structalign: print field-aligned struct reorderings (no file changes)\n\n")
fmt.Fprintf(a.Stderr, "usage: structalign [flags] [packages]\n\n")
Expand Down Expand Up @@ -197,10 +197,16 @@ func (a *App) Run(args []string) int {
}
}

if opt.sort && !opt.inspect {
sort.SliceStable(allFindings, func(i, j int) bool {
return savings(allFindings[i]) > savings(allFindings[j])
})
if opt.sort {
if opt.inspect {
sort.SliceStable(allLayouts, func(i, j int) bool {
return allLayouts[i].Total > allLayouts[j].Total
})
} else {
sort.SliceStable(allFindings, func(i, j int) bool {
return savings(allFindings[i]) > savings(allFindings[j])
})
}
}

var total int
Expand Down
47 changes: 47 additions & 0 deletions internal/app/sort_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,50 @@ func TestRunDiffDefaultOrderUnchanged(t *testing.T) {
assert.Less(t, strings.Index(s, "Small"), strings.Index(s, "Big"),
"without -sort, source order (Small before Big) is preserved")
}

// inspectSortSrc names the small struct alphabetically first (Alpha, 24 bytes)
// and the large one last (Zeta, 40 bytes), so alphabetical order (the inspect
// default) and size-descending order genuinely differ.
const inspectSortSrc = `package sample

type Alpha struct {
A bool
B int64
C bool
}

type Zeta struct {
A bool
B int64
C bool
D int64
E bool
}
`

func inspectSortApp(t *testing.T, out, errb *bytes.Buffer) *app.App {
t.Helper()
tgt := testutil.Target(t, inspectSortSrc)
ml := mocks.NewLoader(t)
ml.EXPECT().Load(mock.Anything).Return([]common.Target{tgt}, nil)
return &app.App{Loader: ml, Aligner: align.New(), Inspector: layout.New(), Stdout: out, Stderr: errb}
}

func TestRunSortInspectOrdersBySize(t *testing.T) {
var out, errb bytes.Buffer
a := inspectSortApp(t, &out, &errb)
code := a.Run([]string{"-inspect", "-sort", "pkg"})
assert.Equal(t, 0, code)
s := out.String()
assert.Less(t, strings.Index(s, "type Zeta"), strings.Index(s, "type Alpha"),
"with -inspect -sort, the larger struct (Zeta, 40) renders before Alpha (24)")
}

func TestRunInspectDefaultOrderUnchanged(t *testing.T) {
var out, errb bytes.Buffer
a := inspectSortApp(t, &out, &errb)
_ = a.Run([]string{"-inspect", "pkg"})
s := out.String()
assert.Less(t, strings.Index(s, "type Alpha"), strings.Index(s, "type Zeta"),
"without -sort, inspect keeps its default (alphabetical) order: Alpha before Zeta")
}
Loading