Skip to content

Commit d08cb3b

Browse files
authored
Merge pull request #48 from github/better-json
Generate better JSON output
2 parents 81349c9 + 17eb18b commit d08cb3b

File tree

9 files changed

+410
-254
lines changed

9 files changed

+410
-254
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ at the command line to view the contents of the object. (Use `--names=none` if y
173173

174174
By default, only statistics above a minimal level of concern are reported. Use `--verbose` (as above) to request that all statistics be output. Use `--threshold=<value>` to suppress the reporting of statistics below a specified level of concern. (`<value>` is interpreted as a numerical value corresponding to the number of asterisks.) Use `--critical` to report only statistics with a critical level of concern (equivalent to `--threshold=30`).
175175

176-
If you'd like the output in machine-readable format, including exact numbers, use the `--json` option.
176+
If you'd like the output in machine-readable format, including exact numbers, use the `--json` option. You can use `--json-version=1` or `--json-version=2` to choose between old and new style JSON output.
177177

178178
To get a list of other options, run
179179

counts/counts.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ func NewCount32(n uint64) Count32 {
1414
return Count32(n)
1515
}
1616

17-
func (n Count32) ToUint64() uint64 {
18-
return uint64(n)
17+
func (n Count32) ToUint64() (uint64, bool) {
18+
return uint64(n), n == math.MaxUint32
1919
}
2020

2121
// Return the sum of two Count32s, capped at math.MaxUint32.
@@ -62,8 +62,8 @@ func NewCount64(n uint64) Count64 {
6262
return Count64(n)
6363
}
6464

65-
func (n Count64) ToUint64() uint64 {
66-
return uint64(n)
65+
func (n Count64) ToUint64() (uint64, bool) {
66+
return uint64(n), n == math.MaxUint64
6767
}
6868

6969
// Return the sum of two Count64s, capped at math.MaxUint64.

counts/counts_test.go

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,43 @@ import (
1111
func TestCount32(t *testing.T) {
1212
assert := assert.New(t)
1313

14+
var value uint64
15+
var overflow bool
16+
1417
c := counts.NewCount32(0)
15-
assert.Equalf(uint64(0), c.ToUint64(), "NewCount32(0).ToUint64() should be 0")
18+
value, overflow = c.ToUint64()
19+
assert.Equalf(uint64(0), value, "NewCount32(0).ToUint64() should be 0")
20+
assert.False(overflow, "NewCount32(0).ToUint64() does not overflow")
1621

1722
c.Increment(counts.Count32(0xf0000000))
18-
assert.Equalf(uint64(0xf0000000), c.ToUint64(), "Count32(0xf0000000).ToUint64() value")
23+
value, overflow = c.ToUint64()
24+
assert.Equalf(uint64(0xf0000000), value, "Count32(0xf0000000).ToUint64() value")
25+
assert.False(overflow, "NewCount32(0xf0000000).ToUint64() does not overflow")
1926

2027
c.Increment(counts.Count32(0xf0000000))
21-
assert.Equalf(uint64(0xffffffff), c.ToUint64(), "Count32(0xffffffff).ToUint64() value")
28+
value, overflow = c.ToUint64()
29+
assert.Equalf(uint64(0xffffffff), value, "Count32(0xffffffff).ToUint64() value")
30+
assert.True(overflow, "NewCount32(0xffffffff).ToUint64() overflows")
2231
}
2332

2433
func TestCount64(t *testing.T) {
2534
assert := assert.New(t)
2635

36+
var value uint64
37+
var overflow bool
38+
2739
c := counts.NewCount64(0)
28-
assert.Equalf(uint64(0), c.ToUint64(), "NewCount64(0).ToUint64() should be 0")
40+
value, overflow = c.ToUint64()
41+
assert.Equalf(uint64(0), value, "NewCount64(0).ToUint64() should be 0")
42+
assert.False(overflow, "NewCount64(0).ToUint64() does not overflow")
2943

3044
c.Increment(counts.Count64(0xf000000000000000))
31-
assert.Equalf(uint64(0xf000000000000000), c.ToUint64(), "Count64(0xf000000000000000).ToUint64() value")
45+
value, overflow = c.ToUint64()
46+
assert.Equalf(uint64(0xf000000000000000), value, "Count64(0xf000000000000000).ToUint64() value")
47+
assert.False(overflow, "NewCount64(0xf000000000000000).ToUint64() does not overflow")
3248

3349
c.Increment(counts.Count64(0xf000000000000000))
34-
assert.Equalf(uint64(0xffffffffffffffff), c.ToUint64(), "Count64(0xffffffffffffffff).ToUint64() value")
50+
value, overflow = c.ToUint64()
51+
assert.Equalf(uint64(0xffffffffffffffff), value, "Count64(0xffffffffffffffff).ToUint64() value")
52+
assert.True(overflow, "NewCount64(0xffffffffffffffff).ToUint64() overflows")
3553
}

counts/human.go

Lines changed: 37 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,51 +2,61 @@ package counts
22

33
import (
44
"fmt"
5-
"math"
65
)
76

8-
type Prefix struct {
9-
Name string
10-
Multiplier uint64
7+
// A quantity that can be made human-readable using Human().
8+
type Humanable interface {
9+
// Return the value as a uint64, and a boolean telling whether it
10+
// overflowed.
11+
ToUint64() (uint64, bool)
1112
}
1213

13-
type Humaner interface {
14-
Human([]Prefix, string) (string, string)
15-
ToUint64() uint64
14+
// An object that can format a Humanable in human-readable format.
15+
type Humaner struct {
16+
name string
17+
prefixes []Prefix
1618
}
1719

18-
var MetricPrefixes []Prefix
20+
type Prefix struct {
21+
Name string
22+
Multiplier uint64
23+
}
1924

20-
func init() {
21-
MetricPrefixes = []Prefix{
25+
var Metric = Humaner{
26+
name: "metric",
27+
prefixes: []Prefix{
2228
{"", 1},
2329
{"k", 1e3},
2430
{"M", 1e6},
2531
{"G", 1e9},
2632
{"T", 1e12},
2733
{"P", 1e15},
28-
}
34+
},
2935
}
3036

31-
var BinaryPrefixes []Prefix
32-
33-
func init() {
34-
BinaryPrefixes = []Prefix{
37+
var Binary = Humaner{
38+
name: "binary",
39+
prefixes: []Prefix{
3540
{"", 1 << (10 * 0)},
3641
{"Ki", 1 << (10 * 1)},
3742
{"Mi", 1 << (10 * 2)},
3843
{"Gi", 1 << (10 * 3)},
3944
{"Ti", 1 << (10 * 4)},
4045
{"Pi", 1 << (10 * 5)},
41-
}
46+
},
4247
}
4348

44-
// Format values, aligned, in `len(unit) + 10` or fewer characters
45-
// (except for extremely large numbers).
46-
func Human(n uint64, prefixes []Prefix, unit string) (string, string) {
47-
prefix := prefixes[0]
49+
func (h *Humaner) Name() string {
50+
return h.name
51+
}
52+
53+
// Format n, aligned, in `len(unit) + 10` or fewer characters (except
54+
// for extremely large numbers).
55+
func (h *Humaner) FormatNumber(n uint64, unit string) (string, string) {
56+
prefix := h.prefixes[0]
57+
4858
wholePart := n
49-
for _, p := range prefixes {
59+
for _, p := range h.prefixes {
5060
w := n / p.Multiplier
5161
if w >= 1 {
5262
wholePart = w
@@ -72,18 +82,13 @@ func Human(n uint64, prefixes []Prefix, unit string) (string, string) {
7282
}
7383
}
7484

75-
func (n Count32) Human(prefixes []Prefix, unit string) (string, string) {
76-
if n == math.MaxUint32 {
85+
// Format values, aligned, in `len(unit) + 10` or fewer characters
86+
// (except for extremely large numbers).
87+
func (h *Humaner) Format(value Humanable, unit string) (string, string) {
88+
n, overflow := value.ToUint64()
89+
if overflow {
7790
return "∞", unit
78-
} else {
79-
return Human(uint64(n), prefixes, unit)
8091
}
81-
}
8292

83-
func (n Count64) Human(prefixes []Prefix, unit string) (string, string) {
84-
if n == math.MaxUint64 {
85-
return "∞", unit
86-
} else {
87-
return Human(uint64(n), prefixes, unit)
88-
}
93+
return h.FormatNumber(n, unit)
8994
}

counts/human_test.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -52,18 +52,18 @@ func TestMetric(t *testing.T) {
5252
{12345678900000000000, "12346", "Pcd"}, // Not ideal, but ok
5353
{0xffffffffffffffff, "18447", "Pcd"}, // Not ideal, but ok
5454
} {
55-
number, unit := counts.Human(ht.n, counts.MetricPrefixes, "cd")
55+
number, unit := counts.Metric.FormatNumber(ht.n, "cd")
5656
assert.Equalf(ht.number, number, "Number for %d in metric", ht.n)
5757
assert.Equalf(ht.unit, unit, "Unit for %d in metric", ht.n)
5858
if ht.n < 0xffffffff {
5959
c := counts.NewCount32(ht.n)
60-
number, unit := c.Human(counts.MetricPrefixes, "cd")
60+
number, unit := counts.Metric.Format(c, "cd")
6161
assert.Equalf(ht.number, number, "Number for Count32(%d) in metric", ht.n)
6262
assert.Equalf(ht.unit, unit, "Unit for Count32(%d) in metric", ht.n)
6363
}
6464
if ht.n < 0xffffffffffffffff {
6565
c := counts.NewCount64(ht.n)
66-
number, unit := c.Human(counts.MetricPrefixes, "cd")
66+
number, unit := counts.Metric.Format(c, "cd")
6767
assert.Equalf(ht.number, number, "Number for Count64(%d) in metric", ht.n)
6868
assert.Equalf(ht.unit, unit, "Unit for Count64(%d) in metric", ht.n)
6969
}
@@ -91,18 +91,18 @@ func TestBinary(t *testing.T) {
9191
{1152921504606846976, "1024", "PiB"},
9292
{0xffffffffffffffff, "16384", "PiB"},
9393
} {
94-
number, unit := counts.Human(ht.n, counts.BinaryPrefixes, "B")
94+
number, unit := counts.Binary.FormatNumber(ht.n, "B")
9595
assert.Equalf(ht.number, number, "Number for %d in binary", ht.n)
9696
assert.Equalf(ht.unit, unit, "Unit for %d in binary", ht.n)
9797
if ht.n < 0xffffffff {
9898
c := counts.NewCount32(ht.n)
99-
number, unit := c.Human(counts.BinaryPrefixes, "B")
99+
number, unit := counts.Binary.Format(c, "B")
100100
assert.Equalf(ht.number, number, "Number for Count32(%d) in binary", ht.n)
101101
assert.Equalf(ht.unit, unit, "Unit for Count32(%d) in binary", ht.n)
102102
}
103103
if ht.n < 0xffffffffffffffff {
104104
c := counts.NewCount64(ht.n)
105-
number, unit := c.Human(counts.BinaryPrefixes, "B")
105+
number, unit := counts.Binary.Format(c, "B")
106106
assert.Equalf(ht.number, number, "Number for Count64(%d) in binary", ht.n)
107107
assert.Equalf(ht.unit, unit, "Unit for Count64(%d) in binary", ht.n)
108108
}
@@ -113,16 +113,16 @@ func TestLimits32(t *testing.T) {
113113
assert := assert.New(t)
114114

115115
c := counts.NewCount32(0xffffffff)
116-
number, unit := c.Human(counts.MetricPrefixes, "cd")
117-
assert.Equalf("∞", number, "Number for Count32(%d) in metric", c.ToUint64())
118-
assert.Equalf("cd", unit, "Unit for Count32(%d) in metric", c.ToUint64())
116+
number, unit := counts.Metric.Format(c, "cd")
117+
assert.Equalf("∞", number, "Number for Count32(0xffffffff) in metric")
118+
assert.Equalf("cd", unit, "Unit for Count32(0xffffffff) in metric")
119119
}
120120

121121
func TestLimits64(t *testing.T) {
122122
assert := assert.New(t)
123123

124124
c := counts.NewCount64(0xffffffffffffffff)
125-
number, unit := c.Human(counts.MetricPrefixes, "B")
126-
assert.Equalf("∞", number, "Number for Count64(%d) in metric", c.ToUint64())
127-
assert.Equalf("B", unit, "Unit for Count64(%d) in metric", c.ToUint64())
125+
number, unit := counts.Metric.Format(c, "B")
126+
assert.Equalf("∞", number, "Number for Count64(0xffffffffffffffff) in metric")
127+
assert.Equalf("B", unit, "Unit for Count64(0xffffffffffffffff) in metric")
128128
}

git-sizer.go

Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -60,57 +60,68 @@ func mainImplementation() error {
6060
var nameStyle sizes.NameStyle = sizes.NameStyleFull
6161
var cpuprofile string
6262
var jsonOutput bool
63+
var jsonVersion uint
6364
var threshold sizes.Threshold = 1
6465
var progress bool
6566
var version bool
6667

67-
pflag.BoolVar(&processBranches, "branches", false, "process all branches")
68-
pflag.BoolVar(&processTags, "tags", false, "process all tags")
69-
pflag.BoolVar(&processRemotes, "remotes", false, "process all remote-tracking branches")
68+
flags := pflag.NewFlagSet("", pflag.ContinueOnError)
7069

71-
pflag.VarP(
70+
flags.BoolVar(&processBranches, "branches", false, "process all branches")
71+
flags.BoolVar(&processTags, "tags", false, "process all tags")
72+
flags.BoolVar(&processRemotes, "remotes", false, "process all remote-tracking branches")
73+
74+
flags.VarP(
7275
sizes.NewThresholdFlagValue(&threshold, 0),
7376
"verbose", "v", "report all statistics, whether concerning or not",
7477
)
75-
pflag.Lookup("verbose").NoOptDefVal = "true"
78+
flags.Lookup("verbose").NoOptDefVal = "true"
7679

77-
pflag.Var(
80+
flags.Var(
7881
&threshold, "threshold",
7982
"minimum level of concern (i.e., number of stars) that should be\n"+
8083
" reported",
8184
)
8285

83-
pflag.Var(
86+
flags.Var(
8487
sizes.NewThresholdFlagValue(&threshold, 30),
8588
"critical", "only report critical statistics",
8689
)
87-
pflag.Lookup("critical").NoOptDefVal = "true"
90+
flags.Lookup("critical").NoOptDefVal = "true"
8891

89-
pflag.Var(
92+
flags.Var(
9093
&nameStyle, "names",
9194
"display names of large objects in the specified `style`:\n"+
9295
" --names=none omit footnotes entirely\n"+
9396
" --names=hash show only the SHA-1s of objects\n"+
9497
" --names=full show full names",
9598
)
9699

97-
pflag.BoolVarP(&jsonOutput, "json", "j", false, "output results in JSON format")
100+
flags.BoolVarP(&jsonOutput, "json", "j", false, "output results in JSON format")
101+
flags.UintVar(&jsonVersion, "json-version", 1, "JSON format version to output (1 or 2)")
98102

99103
atty, err := isatty.Isatty(os.Stderr.Fd())
100104
if err != nil {
101105
atty = false
102106
}
103-
pflag.BoolVar(&progress, "progress", atty, "report progress to stderr")
104-
pflag.BoolVar(&version, "version", false, "report the git-sizer version number")
105-
pflag.Var(&NegatedBoolValue{&progress}, "no-progress", "suppress progress output")
106-
pflag.Lookup("no-progress").NoOptDefVal = "true"
107+
flags.BoolVar(&progress, "progress", atty, "report progress to stderr")
108+
flags.BoolVar(&version, "version", false, "report the git-sizer version number")
109+
flags.Var(&NegatedBoolValue{&progress}, "no-progress", "suppress progress output")
110+
flags.Lookup("no-progress").NoOptDefVal = "true"
111+
112+
flags.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to file")
113+
flags.MarkHidden("cpuprofile")
107114

108-
pflag.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to file")
109-
pflag.CommandLine.MarkHidden("cpuprofile")
115+
flags.SortFlags = false
110116

111-
pflag.CommandLine.SortFlags = false
117+
err = flags.Parse(os.Args[1:])
118+
if err != nil {
119+
return err
120+
}
112121

113-
pflag.Parse()
122+
if jsonOutput && !(jsonVersion == 1 || jsonVersion == 2) {
123+
return fmt.Errorf("JSON version must be 1 or 2")
124+
}
114125

115126
if cpuprofile != "" {
116127
f, err := os.Create(cpuprofile)
@@ -130,7 +141,7 @@ func mainImplementation() error {
130141
return nil
131142
}
132143

133-
args := pflag.Args()
144+
args := flags.Args()
134145

135146
if len(args) != 0 {
136147
return errors.New("excess arguments")
@@ -167,11 +178,20 @@ func mainImplementation() error {
167178
}
168179

169180
if jsonOutput {
170-
s, err := json.MarshalIndent(historySize, "", " ")
181+
var j []byte
182+
var err error
183+
switch jsonVersion {
184+
case 1:
185+
j, err = json.MarshalIndent(historySize, "", " ")
186+
case 2:
187+
j, err = historySize.JSON(threshold, nameStyle)
188+
default:
189+
return fmt.Errorf("JSON version must be 1 or 2")
190+
}
171191
if err != nil {
172192
return fmt.Errorf("could not convert %v to json: %s", historySize, err)
173193
}
174-
fmt.Printf("%s\n", s)
194+
fmt.Printf("%s\n", j)
175195
} else {
176196
io.WriteString(os.Stdout, historySize.TableString(threshold, nameStyle))
177197
}

git/git.go

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -310,17 +310,11 @@ func PrefixFilter(prefix string) ReferenceFilter {
310310
}
311311

312312
var (
313-
BranchesFilter ReferenceFilter
314-
TagsFilter ReferenceFilter
315-
RemotesFilter ReferenceFilter
313+
BranchesFilter ReferenceFilter = PrefixFilter("refs/heads/")
314+
TagsFilter ReferenceFilter = PrefixFilter("refs/tags/")
315+
RemotesFilter ReferenceFilter = PrefixFilter("refs/remotes/")
316316
)
317317

318-
func init() {
319-
BranchesFilter = PrefixFilter("refs/heads/")
320-
TagsFilter = PrefixFilter("refs/tags/")
321-
RemotesFilter = PrefixFilter("refs/remotes/")
322-
}
323-
324318
func notNilFilters(filters ...ReferenceFilter) []ReferenceFilter {
325319
var ret []ReferenceFilter
326320
for _, filter := range filters {

0 commit comments

Comments
 (0)